diff --git a/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala b/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala index f3a6bd72..9467d663 100644 --- a/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala +++ b/src/main/scala/millfork/assembly/z80/opt/AlwaysGoodI80Optimizations.scala @@ -419,7 +419,7 @@ object AlwaysGoodI80Optimizations { }, //20 (Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~ - (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer)).* ~ + (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer) & Not(Changes(DE))).* ~ (Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.DE)) ~~> { code => ZLine.ld8(ZRegister.D, ZRegister.H) :: ZLine.ld8(ZRegister.E, ZRegister.L) :: @@ -427,7 +427,7 @@ object AlwaysGoodI80Optimizations { }, //21 (Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.HL) & DoesntMatterWhatItDoesWith(ZRegister.BC)) ~ - (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer)).* ~ + (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer) & Not(Changes(BC))).* ~ (Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.BC)) ~~> { code => ZLine.ld8(ZRegister.B, ZRegister.H) :: ZLine.ld8(ZRegister.C, ZRegister.L) :: @@ -435,7 +435,7 @@ object AlwaysGoodI80Optimizations { }, //22 (Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~ - (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer)).* ~ + (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer) & Not(Changes(HL))).* ~ (Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.HL)) ~~> { code => ZLine.ld8(ZRegister.H, ZRegister.D) :: ZLine.ld8(ZRegister.L, ZRegister.E) :: @@ -443,7 +443,7 @@ object AlwaysGoodI80Optimizations { }, //23 (Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.HL)) ~ - (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer)).* ~ + (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer) & Not(Changes(HL))).* ~ (Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.HL)) ~~> { code => ZLine.ld8(ZRegister.H, ZRegister.B) :: ZLine.ld8(ZRegister.L, ZRegister.C) :: @@ -451,7 +451,7 @@ object AlwaysGoodI80Optimizations { }, //24 (Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.BC) & DoesntMatterWhatItDoesWith(ZRegister.DE)) ~ - (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer)).* ~ + (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer) & Not(Changes(DE))).* ~ (Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.DE)) ~~> { code => ZLine.ld8(ZRegister.D, ZRegister.B) :: ZLine.ld8(ZRegister.E, ZRegister.C) :: @@ -459,7 +459,7 @@ object AlwaysGoodI80Optimizations { }, //25 (Elidable & HasOpcode(PUSH) & HasRegisterParam(ZRegister.DE) & DoesntMatterWhatItDoesWith(ZRegister.BC)) ~ - (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer)).* ~ + (Linear & Not(HasOpcodeIn(Set(POP, PUSH))) & Not(ReadsStackPointer) & Not(Changes(BC))).* ~ (Elidable & HasOpcode(POP) & HasRegisterParam(ZRegister.BC)) ~~> { code => ZLine.ld8(ZRegister.B, ZRegister.D) :: ZLine.ld8(ZRegister.C, ZRegister.E) :: diff --git a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala index e2a638e8..bb833fd7 100644 --- a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala +++ b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala @@ -99,8 +99,10 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte } // generic warnings: stmt match { - case ExpressionStatement(expr@FunctionCallExpression("strzlen" | "putstrz" | "strzcmp" | "strzcopy", params)) => + case ExpressionStatement(expr@FunctionCallExpression("strzlen" | "putstrz" | "strzcmp" | "strzcopy" | "strzpaste", params)) => for (param <- params) checkIfNullTerminated(ctx, stmt, param) + case ExpressionStatement(expr@FunctionCallExpression("pstrlen" | "putpstr" | "pstrcmp" | "pstrcopy" | "pstrpaste", params)) => + for (param <- params) checkIfLengthPrefixed(ctx, stmt, param) case ExpressionStatement(expr@FunctionCallExpression(f, List(VariableExpression(v)))) if hiddenEffectFreeFunctions(f)=> val volatile = ctx.env.maybeGet[ThingInMemory](v).fold(false)(_.isVolatile) @@ -276,14 +278,34 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte val TERMINATOR = ctx.options.platform.defaultCodec.stringTerminator.head param match { case TextLiteralExpression(ch) => - ch.last match { - case LiteralExpression(TERMINATOR, _) => //ok + ch.lastOption match { + case Some(LiteralExpression(TERMINATOR, _)) => //ok case _ => ctx.log.warn("Passing a non-null-terminated string to a function that expects a null-terminated string.", stmt.position) } case _ => } } + private def checkIfLengthPrefixed(ctx: CompilationContext, stmt: ExecutableStatement, param: Expression): Unit = { + if (!ctx.options.flag(CompilationFlag.BuggyCodeWarning)) return + val TERMINATOR = ctx.options.platform.defaultCodec.stringTerminator.head + param match { + case TextLiteralExpression(ch) => + if (ch.headOption match { + case Some(LiteralExpression(length, _)) if length == ch.size - 1 => false + case Some(LiteralExpression(length, _)) if length == ch.size + 2 => + ch.lastOption match { + case Some(LiteralExpression(TERMINATOR, _)) => false + case _ => true + } + case _ => true + }) { + ctx.log.warn("Passing a non-length-prefixed string to a function that expects a length-prefixed string.", stmt.position) + } + case _ => + } + } + def search(expr: Expression, cv: VV): VV = { expr match { case FunctionCallExpression(op, List(VariableExpression(v), arg)) if op.endsWith("=") && op != "<=" && op != ">=" =>