diff --git a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala index 321c1f6a..6f72d754 100644 --- a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala @@ -543,6 +543,7 @@ object AbstractExpressionCompiler { def checkAssignmentTypeAndGetSourceType(ctx: CompilationContext, source: Expression, target: LhsExpression): Type = { val sourceType = getExpressionType(ctx, source) + if (target == BlackHoleExpression) return sourceType val targetType = getExpressionType(ctx, target) if (!sourceType.isAssignableTo(targetType)) { ctx.log.error(s"Cannot assign `$sourceType` to `$targetType`", target.position.orElse(source.position)) diff --git a/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala index ce7b3f97..079bab9a 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala @@ -3,7 +3,7 @@ package millfork.compiler.m6809 import millfork.assembly.BranchingOpcodeMapping import millfork.assembly.m6809.{MLine, NonExistent} import millfork.compiler.{AbstractCompiler, AbstractExpressionCompiler, AbstractStatementCompiler, BranchSpec, CompilationContext} -import millfork.node.{Assignment, BreakStatement, ContinueStatement, DoWhileStatement, ExecutableStatement, Expression, ExpressionStatement, ForEachStatement, ForStatement, IfStatement, M6809AssemblyStatement, ReturnDispatchStatement, ReturnStatement, VariableExpression, WhileStatement} +import millfork.node.{Assignment, BlackHoleExpression, BreakStatement, ContinueStatement, DoWhileStatement, ExecutableStatement, Expression, ExpressionStatement, ForEachStatement, ForStatement, IfStatement, M6809AssemblyStatement, ReturnDispatchStatement, ReturnStatement, VariableExpression, WhileStatement} import millfork.assembly.m6809.MOpcode._ import millfork.env.{FatBooleanType, Label, ThingInMemory} @@ -43,6 +43,7 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] { ??? } case Assignment(destination, source) => + if (destination == BlackHoleExpression) return M6809ExpressionCompiler.compile(ctx, source, MExpressionTarget.NOTHING, BranchSpec.None) -> Nil val destinationType = AbstractExpressionCompiler.getExpressionType(ctx, destination) AbstractExpressionCompiler.checkAssignmentType(ctx, source, destinationType) destinationType.size match { diff --git a/src/main/scala/millfork/compiler/mos/MosExpressionCompiler.scala b/src/main/scala/millfork/compiler/mos/MosExpressionCompiler.scala index b0495136..cf6ed482 100644 --- a/src/main/scala/millfork/compiler/mos/MosExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/mos/MosExpressionCompiler.scala @@ -2038,6 +2038,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] { def compileAssignment(ctx: CompilationContext, source: Expression, target: LhsExpression): List[AssemblyLine] = { val env = ctx.env val sourceType = AbstractExpressionCompiler.checkAssignmentTypeAndGetSourceType(ctx, source, target) + if (target == BlackHoleExpression) return compile(ctx, source, None, NoBranching) val lhsType = AbstractExpressionCompiler.getExpressionType(ctx, target) val b = env.get[Type]("byte") val w = env.get[Type]("word") diff --git a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala index ea10e537..c3dc2a09 100644 --- a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala +++ b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala @@ -94,6 +94,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] { case s: LabelStatement => List(ZLine.label(env.prefix + s.name)) -> Nil case Assignment(destination, source) => + if (destination == BlackHoleExpression) return Z80ExpressionCompiler.compile(ctx, source, ZExpressionTarget.NOTHING, NoBranching) -> Nil val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source) val targetType = AbstractExpressionCompiler.getExpressionType(ctx, destination) AbstractExpressionCompiler.checkAssignmentType(ctx, source, targetType) diff --git a/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala b/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala index 28052fc6..91d9639c 100644 --- a/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala +++ b/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala @@ -79,29 +79,31 @@ object UnusedGlobalVariables extends NodeOptimization { case x => Some(ExpressionStatement(x).pos(s.position)) } } else Some(s) - case s@Assignment(VariableExpression(n), VariableExpression(_)) => - if (globalsToRemove(n)) Nil else Some(s) + case s@Assignment(VariableExpression(n), expr@VariableExpression(n2)) => + if (globalsToRemove(extractThingName(n))) { + if (globalsToRemove(extractThingName(n))) None else Some(Assignment(BlackHoleExpression, expr).pos(s.position)) + } else Some(s) case s@Assignment(VariableExpression(n), LiteralExpression(_, _)) => - if (globalsToRemove(n)) Nil else Some(s) + if (globalsToRemove(extractThingName(n))) Nil else Some(s) case s@Assignment(VariableExpression(n), expr) => - if (globalsToRemove(n)) Some(ExpressionStatement(expr).pos(s.position)) else Some(s) + if (globalsToRemove(extractThingName(n))) Some(ExpressionStatement(expr).pos(s.position)) else Some(s) case s@Assignment(SeparateBytesExpression(he@VariableExpression(h), le@VariableExpression(l)), expr) => - if (globalsToRemove(h)) { - if (globalsToRemove(l)) + if (globalsToRemove(extractThingName(h))) { + if (globalsToRemove(extractThingName(l))) Some(ExpressionStatement(expr).pos(s.position)) else Some(Assignment(SeparateBytesExpression(BlackHoleExpression, le).pos(he.position), expr).pos(s.position)) } else { - if (globalsToRemove(l)) + if (globalsToRemove(extractThingName(l))) Some(Assignment(SeparateBytesExpression(he, BlackHoleExpression).pos(he.position), expr).pos(s.position)) else Some(s) } case s@Assignment(SeparateBytesExpression(h, le@VariableExpression(l)), expr) => - if (globalsToRemove(l)) Some(Assignment(SeparateBytesExpression(h, BlackHoleExpression).pos(h.position), expr).pos(s.position)) + if (globalsToRemove(extractThingName(l))) Some(Assignment(SeparateBytesExpression(h, BlackHoleExpression).pos(h.position), expr).pos(s.position)) else Some(s) case s@Assignment(SeparateBytesExpression(he@VariableExpression(h), l), expr) => - if (globalsToRemove(h)) Some(Assignment(SeparateBytesExpression(BlackHoleExpression, l).pos(he.position), expr).pos(s.position)) + if (globalsToRemove(extractThingName(h))) Some(Assignment(SeparateBytesExpression(BlackHoleExpression, l).pos(he.position), expr).pos(s.position)) else Some(s) case s: IfStatement => Some(s.copy( diff --git a/src/main/scala/millfork/node/opt/UnusedLocalVariables.scala b/src/main/scala/millfork/node/opt/UnusedLocalVariables.scala index a0d5397c..504d571a 100644 --- a/src/main/scala/millfork/node/opt/UnusedLocalVariables.scala +++ b/src/main/scala/millfork/node/opt/UnusedLocalVariables.scala @@ -81,8 +81,10 @@ object UnusedLocalVariables extends NodeOptimization { case x => Some(ExpressionStatement(x).pos(s.position)) } } else Some(s) - case s@Assignment(VariableExpression(n), VariableExpression(_)) => - if (localsToRemove(extractThingName(n))) Nil else Some(s) + case s@Assignment(VariableExpression(n), expr@VariableExpression(_)) => + if (localsToRemove(extractThingName(n))) { + if (localsToRemove(extractThingName(n))) None else Some(Assignment(BlackHoleExpression, expr).pos(s.position)) + } else Some(s) case s@Assignment(VariableExpression(n), LiteralExpression(_, _)) => if (localsToRemove(extractThingName(n))) Nil else Some(s) case s@Assignment(VariableExpression(n), expr) => diff --git a/src/test/scala/millfork/test/NodeOptimizationSuite.scala b/src/test/scala/millfork/test/NodeOptimizationSuite.scala index 249629c7..f0747096 100644 --- a/src/test/scala/millfork/test/NodeOptimizationSuite.scala +++ b/src/test/scala/millfork/test/NodeOptimizationSuite.scala @@ -1,7 +1,7 @@ package millfork.test import millfork.Cpu -import millfork.test.emu.{EmuNodeOptimizedRun, EmuUnoptimizedCrossPlatformRun} +import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuNodeOptimizedRun, EmuUnoptimizedCrossPlatformRun} import org.scalatest.{FunSuite, Matchers} /** @@ -53,4 +53,19 @@ class NodeOptimizationSuite extends FunSuite with Matchers { m.readLong(0xc000) should equal (0x3000000) } } + + test("Unused global struct variable") { + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)( + """ + | struct p { byte x } + | p b + | void foo (byte x) { + | byte y + | b.x = x + |} + | void main () { + | foo(1) + | } + """.stripMargin) { m => } + } }