mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 03:54:27 +00:00
fix referencesIdentifier() and better removal of unnecessary assignments
This commit is contained in:
parent
f9399bcce7
commit
74db5c6be7
@ -111,7 +111,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
// TODO: use call graph for this?
|
// TODO: use call graph for this?
|
||||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true) {
|
if(decl.value?.referencesIdentifier(listOf(decl.name)) == true || decl.arraysize?.indexExpr?.referencesIdentifier(listOf(decl.name)) == true) {
|
||||||
errors.err("recursive var declaration", decl.position)
|
errors.err("recursive var declaration", decl.position)
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.FunctionCall
|
|
||||||
import prog8.ast.expressions.PrefixExpression
|
|
||||||
import prog8.ast.expressions.TypecastExpression
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
@ -58,8 +56,7 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
val removeDoubleAssignments = deduplicateAssignments(scope.statements)
|
return deduplicateAssignments(scope.statements, scope)
|
||||||
return removeDoubleAssignments.map { IAstModification.Remove(it, scope) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||||
@ -75,8 +72,7 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val removeDoubleAssignments = deduplicateAssignments(block.statements)
|
return deduplicateAssignments(block.statements, block)
|
||||||
return removeDoubleAssignments.map { IAstModification.Remove(it, block) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
@ -99,8 +95,7 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
|
return deduplicateAssignments(subroutine.statements, subroutine)
|
||||||
return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
@ -135,28 +130,117 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deduplicateAssignments(statements: List<Statement>): List<Assignment> {
|
private fun deduplicateAssignments(statements: List<Statement>, scope: IStatementContainer): List<IAstModification> {
|
||||||
// removes 'duplicate' assignments that assign the same target directly after another
|
// removes 'duplicate' assignments that assign the same target directly after another
|
||||||
val linesToRemove = mutableListOf<Assignment>()
|
val linesToRemove = mutableListOf<Assignment>()
|
||||||
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
for (stmtPairs in statements.windowed(2, step = 1)) {
|
fun substituteZeroInBinexpr(expr: BinaryExpression, zero: NumericLiteralValue, assign1: Assignment, assign2: Assignment) {
|
||||||
val assign1 = stmtPairs[0] as? Assignment
|
if(expr.left isSameAs assign2.target) {
|
||||||
val assign2 = stmtPairs[1] as? Assignment
|
// X = X <oper> Right
|
||||||
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
|
linesToRemove.add(assign1)
|
||||||
if (assign1.target.isSameAs(assign2.target, program) && !assign1.target.isIOAddress(compTarget.machine)) {
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray())))
|
expr.left, zero, expr
|
||||||
// only remove the second assignment if its value is a simple expression!
|
))
|
||||||
when(assign2.value) {
|
}
|
||||||
is PrefixExpression,
|
if(expr.right isSameAs assign2.target) {
|
||||||
is BinaryExpression,
|
// X = Left <oper> X
|
||||||
is TypecastExpression,
|
linesToRemove.add(assign1)
|
||||||
is FunctionCall -> { /* don't remove */ }
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
else -> linesToRemove.add(assign1)
|
expr.right, zero, expr
|
||||||
}
|
))
|
||||||
|
}
|
||||||
|
val leftBinExpr = expr.left as? BinaryExpression
|
||||||
|
val rightBinExpr = expr.right as? BinaryExpression
|
||||||
|
if(leftBinExpr!=null && rightBinExpr==null) {
|
||||||
|
if(leftBinExpr.left isSameAs assign2.target) {
|
||||||
|
// X = (X <oper> Right) <oper> Something
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
|
leftBinExpr.left, zero, leftBinExpr
|
||||||
|
))
|
||||||
|
}
|
||||||
|
if(leftBinExpr.right isSameAs assign2.target) {
|
||||||
|
// X = (Left <oper> X) <oper> Something
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
|
leftBinExpr.right, zero, leftBinExpr
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(leftBinExpr==null && rightBinExpr!=null) {
|
||||||
|
if(rightBinExpr.left isSameAs assign2.target) {
|
||||||
|
// X = Something <oper> (X <oper> Right)
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
|
rightBinExpr.left, zero, rightBinExpr
|
||||||
|
))
|
||||||
|
}
|
||||||
|
if(rightBinExpr.right isSameAs assign2.target) {
|
||||||
|
// X = Something <oper> (Left <oper> X)
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
|
rightBinExpr.right, zero, rightBinExpr
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return linesToRemove
|
fun substituteZeroInPrefixexpr(expr: PrefixExpression, zero: NumericLiteralValue, assign1: Assignment, assign2: Assignment) {
|
||||||
|
if(expr.expression isSameAs assign2.target) {
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
|
expr.expression, zero, expr
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun substituteZeroInTypecast(expr: TypecastExpression, zero: NumericLiteralValue, assign1: Assignment, assign2: Assignment) {
|
||||||
|
if(expr.expression isSameAs assign2.target) {
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
|
expr.expression, zero, expr
|
||||||
|
))
|
||||||
|
}
|
||||||
|
val subCast = expr.expression as? TypecastExpression
|
||||||
|
if(subCast!=null && subCast.expression isSameAs assign2.target) {
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
modifications.add(IAstModification.ReplaceNode(
|
||||||
|
subCast.expression, zero, subCast
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
val cvalue1 = assign1.value.constValue(program)
|
||||||
|
if(cvalue1!=null && cvalue1.number==0.0 && assign2.isAugmentable) {
|
||||||
|
val value2 = assign2.value
|
||||||
|
val zero = VarDecl.defaultZero(value2.inferType(program).getOr(DataType.UNDEFINED), value2.position)
|
||||||
|
when(value2) {
|
||||||
|
is BinaryExpression -> substituteZeroInBinexpr(value2, zero, assign1, assign2)
|
||||||
|
is PrefixExpression -> substituteZeroInPrefixexpr(value2, zero, assign1, assign2)
|
||||||
|
is TypecastExpression -> substituteZeroInTypecast(value2, zero, assign1, assign2)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (assign1.target.isSameAs(assign2.target, program) && !assign1.target.isIOAddress(compTarget.machine)) {
|
||||||
|
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(assign2.target.identifier!!.nameInSource))
|
||||||
|
// only remove the second assignment if its value is a simple expression!
|
||||||
|
when(assign2.value) {
|
||||||
|
is PrefixExpression,
|
||||||
|
is BinaryExpression,
|
||||||
|
is TypecastExpression,
|
||||||
|
is FunctionCall -> { /* don't remove */ }
|
||||||
|
else -> linesToRemove.add(assign1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifications + linesToRemove.map { IAstModification.Remove(it, scope) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
|||||||
|
|
||||||
if (binExpr != null && binExpr.operator !in comparisonOperators) {
|
if (binExpr != null && binExpr.operator !in comparisonOperators) {
|
||||||
if (binExpr.left !is BinaryExpression) {
|
if (binExpr.left !is BinaryExpression) {
|
||||||
if (binExpr.right.referencesIdentifier(*assignment.target.identifier!!.nameInSource.toTypedArray())) {
|
if (binExpr.right.referencesIdentifier(assignment.target.identifier!!.nameInSource)) {
|
||||||
// the right part of the expression contains the target variable itself.
|
// the right part of the expression contains the target variable itself.
|
||||||
// we can't 'split' it trivially because the variable will be changed halfway through.
|
// we can't 'split' it trivially because the variable will be changed halfway through.
|
||||||
if(binExpr.operator in associativeOperators) {
|
if(binExpr.operator in associativeOperators) {
|
||||||
|
@ -351,7 +351,7 @@ private fun writeAssembly(program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printAst(program: Program) {
|
fun printProgram(program: Program) {
|
||||||
println()
|
println()
|
||||||
val printer = AstToSourceTextConverter(::print, program)
|
val printer = AstToSourceTextConverter(::print, program)
|
||||||
printer.visit(program)
|
printer.visit(program)
|
||||||
|
@ -508,7 +508,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
fun err(msg: String) = errors.err(msg, decl.position)
|
fun err(msg: String) = errors.err(msg, decl.position)
|
||||||
|
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true)
|
if(decl.value?.referencesIdentifier(listOf(decl.name)) == true || decl.arraysize?.indexExpr?.referencesIdentifier(listOf(decl.name)) == true)
|
||||||
err("recursive var declaration")
|
err("recursive var declaration")
|
||||||
|
|
||||||
// CONST can only occur on simple types (byte, word, float)
|
// CONST can only occur on simple types (byte, word, float)
|
||||||
|
@ -97,21 +97,8 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter)
|
|||||||
|
|
||||||
val nextAssign = assignment.nextSibling() as? Assignment
|
val nextAssign = assignment.nextSibling() as? Assignment
|
||||||
if(nextAssign!=null && nextAssign.target.isSameAs(assignment.target, program)) {
|
if(nextAssign!=null && nextAssign.target.isSameAs(assignment.target, program)) {
|
||||||
// TODO hmm, if both assignments assign to the same thing, can't we just remove the first altogether??? as long as there isn't a function call in the value!
|
|
||||||
|
|
||||||
if(nextAssign.value isSameAs assignment.value)
|
if(nextAssign.value isSameAs assignment.value)
|
||||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
|
|
||||||
if((assignment.value as? NumericLiteralValue)?.number==0.0 && nextAssign.isAugmentable) {
|
|
||||||
val value = nextAssign.value as BinaryExpression
|
|
||||||
if(value.left isSameAs assignment.target) {
|
|
||||||
val assign = Assignment(assignment.target, value.right, nextAssign.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.Remove(assignment, parent as IStatementContainer),
|
|
||||||
IAstModification.ReplaceNode(nextAssign, assign, parent)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
|
@ -12,7 +12,7 @@ import prog8.ast.expressions.IdentifierReference
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.PrefixExpression
|
import prog8.ast.expressions.PrefixExpression
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.printAst
|
import prog8.compiler.printProgram
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compilerinterface.isIOAddress
|
import prog8.compilerinterface.isIOAddress
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
@ -144,7 +144,7 @@ class TestMemory: FunSpec({
|
|||||||
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
wrapWithProgram(listOf(assign))
|
wrapWithProgram(listOf(assign))
|
||||||
printAst(target.definingModule.program)
|
printProgram(target.definingModule.program)
|
||||||
target.isIOAddress(C64Target.machine) shouldBe true
|
target.isIOAddress(C64Target.machine) shouldBe true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import prog8.ast.base.Position
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||||
|
import prog8.compiler.printProgram
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compilerinterface.*
|
import prog8.compilerinterface.*
|
||||||
import prog8tests.helpers.*
|
import prog8tests.helpers.*
|
||||||
@ -344,9 +345,15 @@ class TestOptimization: FunSpec({
|
|||||||
ubyte @shared z1
|
ubyte @shared z1
|
||||||
z1 = 10
|
z1 = 10
|
||||||
ubyte @shared z2
|
ubyte @shared z2
|
||||||
z2 = z1+z2+5
|
z2 = ~z2
|
||||||
ubyte @shared z3
|
ubyte @shared z3
|
||||||
z3 = z1+z3-5
|
z3 = not z3
|
||||||
|
uword @shared z4
|
||||||
|
z4 = (z4 as ubyte)
|
||||||
|
ubyte @shared z5
|
||||||
|
z5 = z1+z5+5
|
||||||
|
ubyte @shared z6
|
||||||
|
z6 = z1+z6-5
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
|
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
|
||||||
@ -354,35 +361,53 @@ class TestOptimization: FunSpec({
|
|||||||
ubyte z1
|
ubyte z1
|
||||||
z1 = 10
|
z1 = 10
|
||||||
ubyte z2
|
ubyte z2
|
||||||
z2 = z1
|
z2 = 255
|
||||||
z2 += 5
|
|
||||||
ubyte z3
|
ubyte z3
|
||||||
z3 = z1
|
z3 = 1
|
||||||
z3 -= 5
|
uword z4
|
||||||
*/
|
z4 = 0
|
||||||
|
ubyte z5
|
||||||
|
z5 = z1
|
||||||
|
z5 += 5
|
||||||
|
ubyte z6
|
||||||
|
z6 = z1
|
||||||
|
z6 -= 5
|
||||||
|
*/
|
||||||
val statements = result.program.entrypoint.statements
|
val statements = result.program.entrypoint.statements
|
||||||
statements.size shouldBe 8
|
statements.size shouldBe 14
|
||||||
val z1decl = statements[0] as VarDecl
|
val z1decl = statements[0] as VarDecl
|
||||||
val z1init = statements[1] as Assignment
|
val z1init = statements[1] as Assignment
|
||||||
val z2decl = statements[2] as VarDecl
|
val z2decl = statements[2] as VarDecl
|
||||||
val z2init = statements[3] as Assignment
|
val z2init = statements[3] as Assignment
|
||||||
val z2plus = statements[4] as Assignment
|
val z3decl = statements[4] as VarDecl
|
||||||
val z3decl = statements[5] as VarDecl
|
val z3init = statements[5] as Assignment
|
||||||
val z3init = statements[6] as Assignment
|
val z4decl = statements[6] as VarDecl
|
||||||
val z3plus = statements[7] as Assignment
|
val z4init = statements[7] as Assignment
|
||||||
|
val z5decl = statements[8] as VarDecl
|
||||||
|
val z5init = statements[9] as Assignment
|
||||||
|
val z5plus = statements[10] as Assignment
|
||||||
|
val z6decl = statements[11] as VarDecl
|
||||||
|
val z6init = statements[12] as Assignment
|
||||||
|
val z6plus = statements[13] as Assignment
|
||||||
|
|
||||||
z1decl.name shouldBe "z1"
|
z1decl.name shouldBe "z1"
|
||||||
z1init.value shouldBe NumericLiteralValue(DataType.UBYTE, 10.0, Position.DUMMY)
|
z1init.value shouldBe NumericLiteralValue(DataType.UBYTE, 10.0, Position.DUMMY)
|
||||||
z2decl.name shouldBe "z2"
|
z2decl.name shouldBe "z2"
|
||||||
z2init.value shouldBe IdentifierReference(listOf("z1"), Position.DUMMY)
|
z2init.value shouldBe NumericLiteralValue(DataType.UBYTE, 255.0, Position.DUMMY)
|
||||||
z2plus.isAugmentable shouldBe true
|
|
||||||
(z2plus.value as BinaryExpression).operator shouldBe "+"
|
|
||||||
(z2plus.value as BinaryExpression).right shouldBe NumericLiteralValue(DataType.UBYTE, 5.0, Position.DUMMY)
|
|
||||||
z3decl.name shouldBe "z3"
|
z3decl.name shouldBe "z3"
|
||||||
z3init.value shouldBe IdentifierReference(listOf("z1"), Position.DUMMY)
|
z3init.value shouldBe NumericLiteralValue(DataType.UBYTE, 1.0, Position.DUMMY)
|
||||||
z3plus.isAugmentable shouldBe true
|
z4decl.name shouldBe "z4"
|
||||||
(z3plus.value as BinaryExpression).operator shouldBe "-"
|
z4init.value shouldBe NumericLiteralValue(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||||
(z3plus.value as BinaryExpression).right shouldBe NumericLiteralValue(DataType.UBYTE, 5.0, Position.DUMMY)
|
z5decl.name shouldBe "z5"
|
||||||
|
z5init.value shouldBe IdentifierReference(listOf("z1"), Position.DUMMY)
|
||||||
|
z5plus.isAugmentable shouldBe true
|
||||||
|
(z5plus.value as BinaryExpression).operator shouldBe "+"
|
||||||
|
(z5plus.value as BinaryExpression).right shouldBe NumericLiteralValue(DataType.UBYTE, 5.0, Position.DUMMY)
|
||||||
|
z6decl.name shouldBe "z6"
|
||||||
|
z6init.value shouldBe IdentifierReference(listOf("z1"), Position.DUMMY)
|
||||||
|
z6plus.isAugmentable shouldBe true
|
||||||
|
(z6plus.value as BinaryExpression).operator shouldBe "-"
|
||||||
|
(z6plus.value as BinaryExpression).right shouldBe NumericLiteralValue(DataType.UBYTE, 5.0, Position.DUMMY)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("force_output option should work with optimizing memwrite assignment") {
|
test("force_output option should work with optimizing memwrite assignment") {
|
||||||
@ -472,4 +497,35 @@ class TestOptimization: FunSpec({
|
|||||||
val result = compileText(C64Target, optimize=false, src, writeAssembly=true).assertSuccess()
|
val result = compileText(C64Target, optimize=false, src, writeAssembly=true).assertSuccess()
|
||||||
result.program.entrypoint.statements.size shouldBe 10
|
result.program.entrypoint.statements.size shouldBe 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("keep the value initializer assignment if the next one depends on it") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword @shared yy
|
||||||
|
yy = 20 ; ok to remove =0 initializer before this
|
||||||
|
uword @shared zz
|
||||||
|
zz += 60 ; NOT ok to remove initializer, should evaluate to 60
|
||||||
|
ubyte @shared xx
|
||||||
|
xx = 6+sin8u(xx) ; NOT ok to remove initializer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
|
||||||
|
printProgram(result.program)
|
||||||
|
/* expected result:
|
||||||
|
uword yy
|
||||||
|
yy = 20
|
||||||
|
uword zz
|
||||||
|
zz = 60
|
||||||
|
ubyte xx
|
||||||
|
xx = 0
|
||||||
|
xx = sin8u(xx)
|
||||||
|
xx += 6
|
||||||
|
*/
|
||||||
|
val stmts = result.program.entrypoint.statements
|
||||||
|
stmts.size shouldBe 8
|
||||||
|
stmts.filterIsInstance<VarDecl>().size shouldBe 3
|
||||||
|
stmts.filterIsInstance<Assignment>().size shouldBe 5
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,6 @@ import io.kotest.matchers.types.instanceOf
|
|||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.printAst
|
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8tests.helpers.ErrorReporterForTests
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.assertFailure
|
import prog8tests.helpers.assertFailure
|
||||||
|
@ -22,7 +22,7 @@ sealed class Expression: Node {
|
|||||||
abstract fun constValue(program: Program): NumericLiteralValue?
|
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
abstract fun accept(visitor: AstWalker, parent: Node)
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
abstract fun referencesIdentifier(vararg scopedName: String): Boolean
|
abstract fun referencesIdentifier(nameInSource: List<String>): Boolean
|
||||||
abstract fun inferType(program: Program): InferredTypes.InferredType
|
abstract fun inferType(program: Program): InferredTypes.InferredType
|
||||||
abstract val isSimple: Boolean
|
abstract val isSimple: Boolean
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName)
|
override fun referencesIdentifier(nameInSource: List<String>) = expression.referencesIdentifier(nameInSource)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val inferred = expression.inferType(program)
|
val inferred = expression.inferType(program)
|
||||||
return when(operator) {
|
return when(operator) {
|
||||||
@ -183,7 +183,7 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = left.referencesIdentifier(*scopedName) || right.referencesIdentifier(*scopedName)
|
override fun referencesIdentifier(nameInSource: List<String>) = left.referencesIdentifier(nameInSource) || right.referencesIdentifier(nameInSource)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val leftDt = left.inferType(program)
|
val leftDt = left.inferType(program)
|
||||||
val rightDt = right.inferType(program)
|
val rightDt = right.inferType(program)
|
||||||
@ -300,7 +300,7 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = arrayvar.referencesIdentifier(*scopedName)
|
override fun referencesIdentifier(nameInSource: List<String>) = arrayvar.referencesIdentifier(nameInSource)
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val target = arrayvar.targetStatement(program)
|
val target = arrayvar.targetStatement(program)
|
||||||
@ -341,7 +341,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName)
|
override fun referencesIdentifier(nameInSource: List<String>) = expression.referencesIdentifier(nameInSource)
|
||||||
override fun inferType(program: Program) = InferredTypes.knownFor(type)
|
override fun inferType(program: Program) = InferredTypes.knownFor(type)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val cv = expression.constValue(program) ?: return null
|
val cv = expression.constValue(program) ?: return null
|
||||||
@ -378,7 +378,7 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
|
|||||||
|
|
||||||
override fun copy() = AddressOf(identifier.copy(), position)
|
override fun copy() = AddressOf(identifier.copy(), position)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = false
|
override fun referencesIdentifier(nameInSource: List<String>) = identifier.nameInSource==nameInSource
|
||||||
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UWORD)
|
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UWORD)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
@ -404,7 +404,7 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = false
|
override fun referencesIdentifier(nameInSource: List<String>) = addressExpression.referencesIdentifier(nameInSource)
|
||||||
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UBYTE)
|
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UBYTE)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
|
||||||
@ -472,7 +472,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
throw FatalAstException("can't replace here")
|
throw FatalAstException("can't replace here")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = false
|
override fun referencesIdentifier(nameInSource: List<String>) = false
|
||||||
override fun constValue(program: Program) = this
|
override fun constValue(program: Program) = this
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
@ -579,7 +579,7 @@ class CharLiteral(val value: Char,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun copy() = CharLiteral(value, altEncoding, position)
|
override fun copy() = CharLiteral(value, altEncoding, position)
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = false
|
override fun referencesIdentifier(nameInSource: List<String>) = false
|
||||||
override fun constValue(program: Program): NumericLiteralValue {
|
override fun constValue(program: Program): NumericLiteralValue {
|
||||||
val bytevalue = program.encoding.encodeString(value.toString(), altEncoding).single()
|
val bytevalue = program.encoding.encodeString(value.toString(), altEncoding).single()
|
||||||
return NumericLiteralValue(DataType.UBYTE, bytevalue.toDouble(), position)
|
return NumericLiteralValue(DataType.UBYTE, bytevalue.toDouble(), position)
|
||||||
@ -614,7 +614,7 @@ class StringLiteralValue(val value: String,
|
|||||||
throw FatalAstException("can't replace here")
|
throw FatalAstException("can't replace here")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = false
|
override fun referencesIdentifier(nameInSource: List<String>) = false
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
@ -650,7 +650,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = value.any { it.referencesIdentifier(*scopedName) }
|
override fun referencesIdentifier(nameInSource: List<String>) = value.any { it.referencesIdentifier(nameInSource) }
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
@ -757,7 +757,7 @@ class RangeExpr(var from: Expression,
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String): Boolean = from.referencesIdentifier(*scopedName) || to.referencesIdentifier(*scopedName)
|
override fun referencesIdentifier(nameInSource: List<String>): Boolean = from.referencesIdentifier(nameInSource) || to.referencesIdentifier(nameInSource)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val fromDt=from.inferType(program)
|
val fromDt=from.inferType(program)
|
||||||
val toDt=to.inferType(program)
|
val toDt=to.inferType(program)
|
||||||
@ -829,8 +829,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String): Boolean =
|
override fun referencesIdentifier(nameInSource: List<String>): Boolean = this.nameInSource==nameInSource
|
||||||
nameInSource.size==scopedName.size && nameInSource.toTypedArray().contentEquals(scopedName)
|
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
return when (val targetStmt = targetStatement(program)) {
|
return when (val targetStmt = targetStatement(program)) {
|
||||||
@ -899,7 +898,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String): Boolean = target.referencesIdentifier(*scopedName) || args.any{it.referencesIdentifier(*scopedName)}
|
override fun referencesIdentifier(nameInSource: List<String>): Boolean = target.referencesIdentifier(nameInSource) || args.any{it.referencesIdentifier(nameInSource)}
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val constVal = constValue(program ,false)
|
val constVal = constValue(program ,false)
|
||||||
|
@ -5,8 +5,6 @@ For next compiler release (7.4)
|
|||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
fix "test augmented expression asmgen" unittest (and textelite compilation)
|
fix "test augmented expression asmgen" unittest (and textelite compilation)
|
||||||
|
|
||||||
TODO certain typecast expressions are also augmentable?? what does this even mean, and does the generated code make a difference???
|
|
||||||
|
|
||||||
optimize TODO in "Add assignment to initialize with zero" in StatementReorderer
|
optimize TODO in "Add assignment to initialize with zero" in StatementReorderer
|
||||||
optimize TODO in after(assignment) in VariousCleanups
|
optimize TODO in after(assignment) in VariousCleanups
|
||||||
optimize: add some more constant folders mentioned in test.p8
|
optimize: add some more constant folders mentioned in test.p8
|
||||||
|
@ -11,42 +11,42 @@ main {
|
|||||||
byte bb
|
byte bb
|
||||||
float fl
|
float fl
|
||||||
|
|
||||||
; TODO add these constant folders:
|
;; TODO add these constant folders:
|
||||||
|
;
|
||||||
; (X + C1) + (Y + C2) => (X + Y) + (C1 + C2)
|
;; (X + C1) + (Y + C2) => (X + Y) + (C1 + C2)
|
||||||
; (X + C1) - (Y + C2) => (X - Y) + (C1 - C2)
|
;; (X + C1) - (Y + C2) => (X - Y) + (C1 - C2)
|
||||||
; ---> together: (X + C1) <plusmin> (Y + C2) => (X <plusmin> Y) + (C1 <plusmin> C2)
|
;; ---> together: (X + C1) <plusmin> (Y + C2) => (X <plusmin> Y) + (C1 <plusmin> C2)
|
||||||
|
;
|
||||||
|
;
|
||||||
; (X - C1) + (Y - C2) => (X + Y) - (C1 + C2)
|
;; (X - C1) + (Y - C2) => (X + Y) - (C1 + C2)
|
||||||
; (X - C1) - (Y - C2) => (X - Y) - (C1 - C2)
|
;; (X - C1) - (Y - C2) => (X - Y) - (C1 - C2)
|
||||||
|
;
|
||||||
xx=6
|
; xx=6
|
||||||
yy=8
|
; yy=8
|
||||||
|
;
|
||||||
yy = (xx+5)+(yy+10)
|
; yy = (xx+5)+(yy+10)
|
||||||
txt.print_ub(yy) ; 29
|
; txt.print_ub(yy) ; 29
|
||||||
txt.nl()
|
; txt.nl()
|
||||||
|
;
|
||||||
xx=100
|
; xx=100
|
||||||
yy=8
|
; yy=8
|
||||||
yy = (xx+5)-(yy+10)
|
; yy = (xx+5)-(yy+10)
|
||||||
txt.print_ub(yy) ; 87
|
; txt.print_ub(yy) ; 87
|
||||||
txt.nl()
|
; txt.nl()
|
||||||
|
;
|
||||||
xx=50
|
; xx=50
|
||||||
yy=40
|
; yy=40
|
||||||
yy = (xx-5)+(yy-10)
|
; yy = (xx-5)+(yy-10)
|
||||||
txt.print_ub(yy) ; 75
|
; txt.print_ub(yy) ; 75
|
||||||
txt.nl()
|
; txt.nl()
|
||||||
|
;
|
||||||
xx=50
|
; xx=50
|
||||||
yy=20
|
; yy=20
|
||||||
yy = (xx-5)-(yy-10)
|
; yy = (xx-5)-(yy-10)
|
||||||
txt.print_ub(yy) ; 35
|
; txt.print_ub(yy) ; 35
|
||||||
txt.nl()
|
; txt.nl()
|
||||||
|
;
|
||||||
repeat {
|
; repeat {
|
||||||
}
|
; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user