From 95e9e1b55059c5d3c4b437c4ddb82462bcdad0e4 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 1 Oct 2020 00:37:09 +0200 Subject: [PATCH] avoid adding unneeded variable initalization assignments. Improved removal of useless double assignments. --- compiler/src/prog8/ast/AstToplevel.kt | 4 +- .../src/prog8/ast/statements/AstStatements.kt | 98 ++++++---- .../compiler/BeforeAsmGenerationAstChanger.kt | 11 +- compiler/src/prog8/compiler/Main.kt | 4 +- .../compiler/target/IMachineDefinition.kt | 2 +- .../target/c64/C64MachineDefinition.kt | 2 +- .../target/cx16/CX16MachineDefinition.kt | 4 +- .../src/prog8/optimizer/StatementOptimizer.kt | 102 +++------- .../src/prog8/optimizer/UnusedCodeRemover.kt | 35 +++- compiler/test/UnitTests.kt | 179 +++++++++++++++++- examples/plasma.p8 | 4 +- examples/test.p8 | 29 ++- 12 files changed, 323 insertions(+), 151 deletions(-) diff --git a/compiler/src/prog8/ast/AstToplevel.kt b/compiler/src/prog8/ast/AstToplevel.kt index 741cdd557..491b7a23e 100644 --- a/compiler/src/prog8/ast/AstToplevel.kt +++ b/compiler/src/prog8/ast/AstToplevel.kt @@ -240,7 +240,7 @@ class Program(val name: String, val modules: MutableList): Node { override fun linkParents(parent: Node) { modules.forEach { - it.linkParents(this) + it.linkParents(namespace) } } @@ -327,8 +327,6 @@ class GlobalNamespace(val modules: List): Node, INameScope { if(symbolFromInnerScope!=null) return symbolFromInnerScope } - val p1 = localContext.parent - val p2 = localContext.parent.parent // lookup something from the module. return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) { diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index b5f9f440d..292ad1e53 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -5,6 +5,7 @@ import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.processing.AstWalker import prog8.ast.processing.IAstVisitor +import prog8.compiler.target.CompilationTarget sealed class Statement : Node { @@ -392,8 +393,8 @@ data class AssignTarget(var identifier: IdentifierReference?, override fun replaceChildNode(node: Node, replacement: Node) { when { - node===identifier -> identifier = replacement as IdentifierReference - node===arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression + node === identifier -> identifier = replacement as IdentifierReference + node === arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression else -> throw FatalAstException("invalid replace") } replacement.parent = this @@ -414,16 +415,16 @@ data class AssignTarget(var identifier: IdentifierReference?, } fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType { - if(identifier!=null) { + if (identifier != null) { val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown() if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype) } - if(arrayindexed!=null) { + if (arrayindexed != null) { return arrayindexed!!.inferType(program) } - if(memoryAddress!=null) + if (memoryAddress != null) return InferredTypes.knownFor(DataType.UBYTE) return InferredTypes.unknown() @@ -431,69 +432,92 @@ data class AssignTarget(var identifier: IdentifierReference?, fun toExpression(): Expression { return when { - identifier!=null -> identifier!! - arrayindexed!=null -> arrayindexed!! - memoryAddress!=null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position) + identifier != null -> identifier!! + arrayindexed != null -> arrayindexed!! + memoryAddress != null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position) else -> throw FatalAstException("invalid assignmenttarget $this") } } infix fun isSameAs(value: Expression): Boolean { return when { - this.memoryAddress!=null -> { + this.memoryAddress != null -> { // if the target is a memory write, and the value is a memory read, they're the same if the address matches - if(value is DirectMemoryRead) + if (value is DirectMemoryRead) this.memoryAddress.addressExpression isSameAs value.addressExpression else false } - this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource - this.arrayindexed!=null -> value is ArrayIndexedExpression && - value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource && - value.arrayspec.constIndex()!=null && - arrayindexed!!.arrayspec.constIndex()!=null && - value.arrayspec.constIndex()==arrayindexed!!.arrayspec.constIndex() + this.identifier != null -> value is IdentifierReference && value.nameInSource == identifier!!.nameInSource + this.arrayindexed != null -> value is ArrayIndexedExpression && + value.identifier.nameInSource == arrayindexed!!.identifier.nameInSource && + value.arrayspec.constIndex() != null && + arrayindexed!!.arrayspec.constIndex() != null && + value.arrayspec.constIndex() == arrayindexed!!.arrayspec.constIndex() else -> false } } fun isSameAs(other: AssignTarget, program: Program): Boolean { - if(this===other) + if (this === other) return true - if(this.identifier!=null && other.identifier!=null) - return this.identifier!!.nameInSource==other.identifier!!.nameInSource - if(this.memoryAddress!=null && other.memoryAddress!=null) { + if (this.identifier != null && other.identifier != null) + return this.identifier!!.nameInSource == other.identifier!!.nameInSource + if (this.memoryAddress != null && other.memoryAddress != null) { val addr1 = this.memoryAddress.addressExpression.constValue(program) val addr2 = other.memoryAddress.addressExpression.constValue(program) - return addr1!=null && addr2!=null && addr1==addr2 + return addr1 != null && addr2 != null && addr1 == addr2 } - if(this.arrayindexed!=null && other.arrayindexed!=null) { - if(this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) { + if (this.arrayindexed != null && other.arrayindexed != null) { + if (this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) { val x1 = this.arrayindexed!!.arrayspec.index.constValue(program) val x2 = other.arrayindexed!!.arrayspec.index.constValue(program) - return x1!=null && x2!=null && x1==x2 + return x1 != null && x2 != null && x1 == x2 } } return false } - fun isNotMemory(namespace: INameScope): Boolean { - if(this.memoryAddress!=null) - return false - if(this.arrayindexed!=null) { - val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace) - if(targetStmt!=null) - return targetStmt.type!= VarDeclType.MEMORY + fun isInRegularRAM(namespace: INameScope): Boolean { + when { + this.memoryAddress != null -> { + return when (this.memoryAddress.addressExpression) { + is NumericLiteralValue -> { + CompilationTarget.instance.machine.isRegularRAMaddress((this.memoryAddress.addressExpression as NumericLiteralValue).number.toInt()) + } + is IdentifierReference -> { + val decl = (this.memoryAddress.addressExpression as IdentifierReference).targetVarDecl(namespace) + if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue) + CompilationTarget.instance.machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) + else + false + } + else -> false + } + } + this.arrayindexed != null -> { + val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace) + return if (targetStmt?.type == VarDeclType.MEMORY) { + val addr = targetStmt.value as? NumericLiteralValue + if (addr != null) + CompilationTarget.instance.machine.isRegularRAMaddress(addr.number.toInt()) + else + false + } else true + } + this.identifier != null -> { + val decl = this.identifier!!.targetVarDecl(namespace)!! + return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue) + CompilationTarget.instance.machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) + else + true + } + else -> return true } - if(this.identifier!=null) { - val targetStmt = this.identifier!!.targetVarDecl(namespace) - if(targetStmt!=null) - return targetStmt.type!= VarDeclType.MEMORY - } - return false } } + class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() { override lateinit var parent: Node diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index 8da7ee1bb..f554b1bcf 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -17,8 +17,13 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E override fun after(decl: VarDecl, parent: Node): Iterable { subroutineVariables.add(Pair(decl.name, decl)) if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) { - // a numeric vardecl without an initial value is initialized with zero. - decl.value = decl.zeroElementValue() + // a numeric vardecl without an initial value is initialized with zero, + // unless there's already an assignment below, that initializes the value + val nextAssign = decl.definingScope().nextSibling(decl) as? Assignment + if(nextAssign!=null && nextAssign.target.isSameAs(IdentifierReference(listOf(decl.name), Position.DUMMY))) + decl.value = null + else + decl.value = decl.zeroElementValue() } return noModifications } @@ -29,7 +34,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E // But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF. if(!assignment.isAugmentable && assignment.target.identifier != null - && assignment.target.isNotMemory(program.namespace)) { + && assignment.target.isInRegularRAM(program.namespace)) { val binExpr = assignment.value as? BinaryExpression if (binExpr != null && binExpr.operator !in comparisonOperators) { if (binExpr.left !is BinaryExpression) { diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 756d0727c..378379054 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -195,7 +195,7 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) { break } - val remover = UnusedCodeRemover(errors) + val remover = UnusedCodeRemover(programAst, errors) remover.visit(programAst) remover.applyModifications() errors.handle() @@ -218,7 +218,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: programAst.processAstBeforeAsmGeneration(errors) errors.handle() - // printAst(programAst) + printAst(programAst) // TODO CompilationTarget.instance.machine.initializeZeropage(compilerOptions) val assembly = CompilationTarget.instance.asmGenerator( diff --git a/compiler/src/prog8/compiler/target/IMachineDefinition.kt b/compiler/src/prog8/compiler/target/IMachineDefinition.kt index fe86770cc..81a121bbd 100644 --- a/compiler/src/prog8/compiler/target/IMachineDefinition.kt +++ b/compiler/src/prog8/compiler/target/IMachineDefinition.kt @@ -35,5 +35,5 @@ internal interface IMachineDefinition { fun getFloatRomConst(number: Double): String? fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) fun launchEmulator(programName: String) - fun isRAMaddress(address: Int): Boolean + fun isRegularRAMaddress(address: Int): Boolean } diff --git a/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt b/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt index 9bd841912..cbabc1b45 100644 --- a/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt +++ b/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt @@ -89,7 +89,7 @@ internal object C64MachineDefinition: IMachineDefinition { } } - override fun isRAMaddress(address: Int): Boolean = (address<0xa000) || (address in 0xc000..0xd000) + override fun isRegularRAMaddress(address: Int): Boolean = address<0xa000 || address in 0xc000..0xcfff override fun initializeZeropage(compilerOptions: CompilationOptions) { zeropage = C64Zeropage(compilerOptions) diff --git a/compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt b/compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt index 916f0faf2..091ce5827 100644 --- a/compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt +++ b/compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt @@ -52,9 +52,7 @@ internal object CX16MachineDefinition: IMachineDefinition { } } - override fun isRAMaddress(address: Int): Boolean { - return address < 0x9000 // TODO put correct Cx16 mem ranges here - } + override fun isRegularRAMaddress(address: Int): Boolean = address < 0x9f00 || address in 0xa000..0xbfff override fun initializeZeropage(compilerOptions: CompilationOptions) { zeropage = CX16Zeropage(compilerOptions) diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index 088abbed2..495c6f0f7 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -49,11 +49,6 @@ internal class StatementOptimizer(private val program: Program, } } - val linesToRemove = deduplicateAssignments(subroutine.statements) - if(linesToRemove.isNotEmpty()) { - linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)} - } - if(subroutine !in callgraph.usedSymbols && !forceOutput) { errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position) return listOf(IAstModification.Remove(subroutine, parent)) @@ -62,11 +57,6 @@ internal class StatementOptimizer(private val program: Program, return noModifications } - override fun after(scope: AnonymousScope, parent: Node): Iterable { - val linesToRemove = deduplicateAssignments(scope.statements) - return linesToRemove.reversed().map { IAstModification.Remove(scope.statements[it], scope) } - } - override fun after(decl: VarDecl, parent: Node): Iterable { val forceOutput = "force_output" in decl.definingBlock().options() if(decl !in callgraph.usedSymbols && !forceOutput) { @@ -447,36 +437,16 @@ internal class StatementOptimizer(private val program: Program, } - fun isSimpleTarget(target: AssignTarget): Boolean { + fun isSimpleTarget(target: AssignTarget, namespace: INameScope): Boolean { return when { - target.identifier!=null -> { - val decl = target.identifier!!.targetVarDecl(program.namespace)!! - return if(decl.type!=VarDeclType.MEMORY) { - if(decl.value is NumericLiteralValue) { - CompilationTarget.instance.machine.isRAMaddress((decl.value as NumericLiteralValue).number.toInt()) - } else { - false - } - } else true - } - target.memoryAddress!=null -> { - return when (target.memoryAddress.addressExpression) { - is NumericLiteralValue -> { - CompilationTarget.instance.machine.isRAMaddress((target.memoryAddress.addressExpression as NumericLiteralValue).number.toInt()) - } - is IdentifierReference -> { - val decl = (target.memoryAddress.addressExpression as IdentifierReference).targetVarDecl(program.namespace)!! - if(decl.value is NumericLiteralValue) { - CompilationTarget.instance.machine.isRAMaddress((decl.value as NumericLiteralValue).number.toInt()) - } else { - false - } - } - else -> false - } - } + target.identifier!=null -> target.isInRegularRAM(namespace) + target.memoryAddress!=null -> target.isInRegularRAM(namespace) target.arrayindexed!=null -> { - target.arrayindexed!!.arrayspec.index is NumericLiteralValue + val index = target.arrayindexed!!.arrayspec.index + if(index is NumericLiteralValue || index is IdentifierReference) + target.isInRegularRAM(namespace) + else + false } else -> false } @@ -487,51 +457,27 @@ internal class StatementOptimizer(private val program: Program, // X = // or X = // split that into X = ; X = X - if(bexpr.operator !in comparisonOperators && !assignment.isAugmentable && isSimpleTarget(assignment.target)) { - if (bexpr.right !is BinaryExpression) { - val firstAssign = Assignment(assignment.target, bexpr.left, assignment.position) - val augExpr = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.right, bexpr.position) - return listOf( - IAstModification.InsertBefore(assignment, firstAssign, parent), - IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) - } else if (bexpr.left !is BinaryExpression && bexpr.operator in associativeOperators) { - val firstAssign = Assignment(assignment.target, bexpr.right, assignment.position) - val augExpr = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.left, bexpr.position) - return listOf( - IAstModification.InsertBefore(assignment, firstAssign, parent), - IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) - } - } + // TODO FIX THIS SPLITTING (IT ENDS UP IN A LOOP SOMETIMES) +// if(bexpr.operator !in comparisonOperators && !assignment.isAugmentable && isSimpleTarget(assignment.target, program.namespace)) { +// if (bexpr.right !is BinaryExpression) { +// val firstAssign = Assignment(assignment.target, bexpr.left, assignment.position) +// val augExpr = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.right, bexpr.position) +// return listOf( +// IAstModification.InsertBefore(assignment, firstAssign, parent), +// IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) +// } else if (bexpr.left !is BinaryExpression && bexpr.operator in associativeOperators) { +// val firstAssign = Assignment(assignment.target, bexpr.right, assignment.position) +// val augExpr = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.left, bexpr.position) +// return listOf( +// IAstModification.InsertBefore(assignment, firstAssign, parent), +// IAstModification.ReplaceNode(assignment.value, augExpr, assignment)) +// } +// } } return noModifications } - private fun deduplicateAssignments(statements: List): MutableList { - // removes 'duplicate' assignments that assign the isSameAs target - val linesToRemove = mutableListOf() - var previousAssignmentLine: Int? = null - for (i in statements.indices) { - val stmt = statements[i] as? Assignment - if (stmt != null && stmt.value is NumericLiteralValue) { - if (previousAssignmentLine == null) { - previousAssignmentLine = i - continue - } else { - val prev = statements[previousAssignmentLine] as Assignment - if (prev.target.isSameAs(stmt.target, program)) { - // get rid of the previous assignment, if the target is not MEMORY - if (prev.target.isNotMemory(program.namespace)) - linesToRemove.add(previousAssignmentLine) - } - previousAssignmentLine = i - } - } else - previousAssignmentLine = null - } - return linesToRemove - } - private fun hasBreak(scope: INameScope): Boolean { class Searcher: IAstVisitor diff --git a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt index d7845138f..6054c286d 100644 --- a/compiler/src/prog8/optimizer/UnusedCodeRemover.kt +++ b/compiler/src/prog8/optimizer/UnusedCodeRemover.kt @@ -4,6 +4,7 @@ import prog8.ast.INameScope import prog8.ast.Node import prog8.ast.Program import prog8.ast.base.ErrorReporter +import prog8.ast.expressions.NumericLiteralValue import prog8.ast.processing.AstWalker import prog8.ast.processing.IAstModification import prog8.ast.statements.* @@ -11,7 +12,7 @@ import prog8.ast.statements.* // TODO remove unneeded assignments such as: cc = 0 ; cc= xbuf ; ... the first can be removed (unless target is not RAM) -internal class UnusedCodeRemover(private val errors: ErrorReporter): AstWalker() { +internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() { override fun before(program: Program, parent: Node): Iterable { val callgraph = CallGraph(program) @@ -69,4 +70,36 @@ internal class UnusedCodeRemover(private val errors: ErrorReporter): AstWalker() else -> errors.warn("unreachable code", next.position) } } + + override fun after(scope: AnonymousScope, parent: Node): Iterable { + val removeDoubleAssignments = deduplicateAssignments(scope.statements) + return removeDoubleAssignments.map { IAstModification.Remove(it, scope) } + } + + override fun after(block: Block, parent: Node): Iterable { + val removeDoubleAssignments = deduplicateAssignments(block.statements) + return removeDoubleAssignments.map { IAstModification.Remove(it, block) } + } + + override fun after(subroutine: Subroutine, parent: Node): Iterable { + val removeDoubleAssignments = deduplicateAssignments(subroutine.statements) + return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) } + } + + // subroutine, anonscope, + private fun deduplicateAssignments(statements: List): List { + // removes 'duplicate' assignments that assign the isSameAs target + val linesToRemove = mutableListOf() + + for (stmtPairs in statements.windowed(2, step = 1)) { + val assign1 = stmtPairs[0] as? Assignment + val assign2 = stmtPairs[1] as? Assignment + if (assign1 != null && assign2 != null) { + if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAM(program.namespace)) + linesToRemove.add(assign1) + } + } + + return linesToRemove + } } diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index 33234e69f..72c44f692 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -5,18 +5,21 @@ import org.hamcrest.Matchers.closeTo import org.hamcrest.Matchers.equalTo import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance -import prog8.ast.base.DataType -import prog8.ast.base.ErrorReporter -import prog8.ast.base.Position -import prog8.ast.expressions.NumericLiteralValue -import prog8.ast.expressions.StringLiteralValue +import prog8.ast.Module +import prog8.ast.Program +import prog8.ast.base.* +import prog8.ast.expressions.* +import prog8.ast.statements.* import prog8.compiler.* +import prog8.compiler.target.C64Target +import prog8.compiler.target.CompilationTarget import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5 import prog8.compiler.target.c64.Petscii import java.io.CharConversionException +import java.nio.file.Path import kotlin.test.* @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -379,3 +382,169 @@ class TestPetscii { assertFalse(abc!=abc) } } + + +class TestMemory { + @Test + fun testInValidRamC64_memory_addresses() { + CompilationTarget.instance = C64Target + + var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY) + var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + var scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertTrue(target.isInRegularRAM(scope)) + + memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertTrue(target.isInRegularRAM(scope)) + + memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertTrue(target.isInRegularRAM(scope)) + + memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertTrue(target.isInRegularRAM(scope)) + + memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertTrue(target.isInRegularRAM(scope)) + } + + @Test + fun testNotInValidRamC64_memory_addresses() { + CompilationTarget.instance = C64Target + + var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY) + var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + var scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertFalse(target.isInRegularRAM(scope)) + + memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertFalse(target.isInRegularRAM(scope)) + + memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertFalse(target.isInRegularRAM(scope)) + + memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY) + target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertFalse(target.isInRegularRAM(scope)) + } + + @Test + fun testInValidRamC64_memory_identifiers() { + CompilationTarget.instance = C64Target + var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR) + assertTrue(target.isInRegularRAM(target.definingScope())) + target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR) + assertFalse(target.isInRegularRAM(target.definingScope())) + target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.CONST) + assertTrue(target.isInRegularRAM(target.definingScope())) + target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.CONST) + assertFalse(target.isInRegularRAM(target.definingScope())) + target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.MEMORY) + assertFalse(target.isInRegularRAM(target.definingScope())) + } + + @Test + private fun createTestProgramForMemoryRefViaVar(address: Int, vartype: VarDeclType): AssignTarget { + val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) + val memexpr = IdentifierReference(listOf("address"), Position.DUMMY) + val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) + val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY) + subroutine.linkParents(ParentSentinel) + return target + } + + @Test + fun testInValidRamC64_memory_expression() { + CompilationTarget.instance = C64Target + val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY) + val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) + val scope = AnonymousScope(mutableListOf(), Position.DUMMY) + assertFalse(target.isInRegularRAM(scope)) + } + + @Test + fun testInValidRamC64_variable() { + CompilationTarget.instance = C64Target + val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY) + val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) + val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) + val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY) + subroutine.linkParents(ParentSentinel) + assertTrue(target.isInRegularRAM(target.definingScope())) + } + + @Test + fun testInValidRamC64_memmap_variable() { + CompilationTarget.instance = C64Target + val address = 0x1000 + val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) + val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) + val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) + val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY) + subroutine.linkParents(ParentSentinel) + assertTrue(target.isInRegularRAM(target.definingScope())) + } + + @Test + fun testNotInValidRamC64_memmap_variable() { + CompilationTarget.instance = C64Target + val address = 0xd020 + val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) + val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) + val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) + val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY) + subroutine.linkParents(ParentSentinel) + assertFalse(target.isInRegularRAM(target.definingScope())) + } + + @Test + fun testInValidRamC64_array() { + CompilationTarget.instance = C64Target + val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY) + val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) + val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) + val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) + val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY) + subroutine.linkParents(ParentSentinel) + assertTrue(target.isInRegularRAM(target.definingScope())) + } + + @Test + fun testInValidRamC64_array_memmapped() { + CompilationTarget.instance = C64Target + val address = 0x1000 + val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) + val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) + val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) + val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) + val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY) + subroutine.linkParents(ParentSentinel) + assertTrue(target.isInRegularRAM(target.definingScope())) + } + + @Test + fun testNotValidRamC64_array_memmapped() { + CompilationTarget.instance = C64Target + val address = 0xe000 + val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) + val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) + val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) + val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) + val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY) + subroutine.linkParents(ParentSentinel) + assertFalse(target.isInRegularRAM(target.definingScope())) + } +} diff --git a/examples/plasma.p8 b/examples/plasma.p8 index adc55b327..d9c24f274 100644 --- a/examples/plasma.p8 +++ b/examples/plasma.p8 @@ -74,12 +74,12 @@ main { } c2A += 2 c2B -= 3 - ubyte cc for y in 24 downto 0 { for x in 39 downto 0 { ; using a temp var here to enable expression optimization that can't be done on a 'problematic' ROM/RAM memory location - ubyte cc = xbuf[x] + ybuf[y] ; TODO should be split!! + ubyte cc + cc = xbuf[x] + ybuf[y] ; TODO should be split!! @(screen) = cc ; this is the fastest way to do this inner part: ; %asm {{ diff --git a/examples/test.p8 b/examples/test.p8 index 7e3bb0208..14cdd188f 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -37,8 +37,8 @@ _saveX .byte 0 sub start() { ; byte bb = 100 ; word ww = 30000 - float ff1 = 1000 - float ff2 = -1000 +; float ff1 = 1000 +; float ff2 = -1000 ubyte[10] xbuf @@ -46,22 +46,21 @@ _saveX .byte 0 ubyte x ubyte y - ubyte cc = xbuf[x] + ybuf[y] ; TODO should be split!! + ubyte cc = xbuf[x] + ybuf[y] ; TODO should be split!! also fix plasma.p8 ubyte cc2 - cc2 = xbuf[x] + ybuf[y] ; will be split correctly? - + cc2 = xbuf[x] + ybuf[y] +cc ; will be split correctly. return - ff1 = 1+((-ff1) *3) - floats.print_f(ff1) - floats.print_f(1+((-1000) *3)) - testX() - ff1 = 1+((-ff2) *3) - floats.print_f(ff1) - floats.print_f(1+((- (-1000)) *3)) - txt.chrout('\n') - testX() - return +; ff1 = 1+((-ff1) *3) +; floats.print_f(ff1) +; floats.print_f(1+((-1000) *3)) +; testX() +; ff1 = 1+((-ff2) *3) +; floats.print_f(ff1) +; floats.print_f(1+((- (-1000)) *3)) +; txt.chrout('\n') +; testX() +; return ; struct Color { ; ubyte red