diff --git a/CHANGELOG.md b/CHANGELOG.md index 607a22ff..700e55a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * Added `breakpoint` macro (#44). +* Added warnings for calling from one segment to another overlapping one. + * 6502: Fixed undocumented mnemonics. * Create output directories when needed (#21) diff --git a/docs/api/command-line.md b/docs/api/command-line.md index cf0dcb47..176514a8 100644 --- a/docs/api/command-line.md +++ b/docs/api/command-line.md @@ -260,6 +260,10 @@ command line options `--inline`, `--dangerous-optimizations` `--fipo` and `--fno ## Warning options +By default, the compiler emits only some of the most important warnings. + * `-Wall` – Enable extra warnings. +* `-Wnone` – Disable all warnings. + * `-Wfatal` – Treat warnings as errors. diff --git a/src/main/scala/millfork/CompilationOptions.scala b/src/main/scala/millfork/CompilationOptions.scala index c9c8f948..596b878f 100644 --- a/src/main/scala/millfork/CompilationOptions.scala +++ b/src/main/scala/millfork/CompilationOptions.scala @@ -409,6 +409,7 @@ object Cpu extends Enumeration { private val alwaysDefaultFlags = Set( VariableOverlap, CompactReturnDispatchParams, FunctionFallthrough, RegisterVariables, FunctionDeduplication, EnableBreakpoints, + NonZeroTerminatedLiteralWarning, CallToOverlappingBankWarning, ) private val mosAlwaysDefaultFlags = alwaysDefaultFlags @@ -564,6 +565,7 @@ object CompilationFlag extends Enumeration { ExtraComparisonWarnings, RorWarning, NonZeroTerminatedLiteralWarning, + CallToOverlappingBankWarning, FatalWarnings, // special options for internal compiler use EnableInternalTestSyntax, diff --git a/src/main/scala/millfork/Main.scala b/src/main/scala/millfork/Main.scala index de33d80b..4e58c938 100644 --- a/src/main/scala/millfork/Main.scala +++ b/src/main/scala/millfork/Main.scala @@ -758,6 +758,10 @@ object Main { CompilationFlag.allWarnings.foldLeft(c) { (c, f) => c.changeFlag(f, true) } }.description("Enable extra warnings.") + flag("-Wnone", "--Wnone").action { c => + CompilationFlag.allWarnings.foldLeft(c) { (c, f) => c.changeFlag(f, false) } + }.description("Disable all warnings.") + flag("-Wfatal", "--Wfatal").action { c => c.changeFlag(CompilationFlag.FatalWarnings, true) }.description("Treat warnings as errors.") diff --git a/src/main/scala/millfork/Platform.scala b/src/main/scala/millfork/Platform.scala index 1e9531d5..29d0d093 100644 --- a/src/main/scala/millfork/Platform.scala +++ b/src/main/scala/millfork/Platform.scala @@ -46,6 +46,19 @@ class Platform( def cpuFamily: CpuFamily.Value = CpuFamily.forType(this.cpu) def isBigEndian: Boolean = CpuFamily.isBigEndian(cpuFamily) + + private def bankStart(bank: String): Int = codeAllocators(bank).startAt min variableAllocators(bank).startAt + + private def bankEndBefore(bank: String): Int = codeAllocators(bank).endBefore max variableAllocators(bank).endBefore + + def isUnsafeToJump(bank1: String, bank2: String): Boolean = { + if (bank1 == bank2) return false + val s1 = bankStart(bank1) + val s2 = bankStart(bank2) + val e1 = bankEndBefore(bank1) + val e2 = bankEndBefore(bank2) + e2 > s1 && s2 < e1 + } } object Platform { diff --git a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala index a0598ebe..3a33b6a5 100644 --- a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala +++ b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala @@ -544,7 +544,18 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte ctx.env.maybeGet[Thing](name) match { case Some(_: MacroFunction) => FunctionCallExpression(name, args.map(arg => optimizeExpr(arg, Map()))).pos(pos) - case _ => + case a => + if (ctx.options.flag(CompilationFlag.CallToOverlappingBankWarning)) { + a match { + case Some(f: FunctionInMemory) => + val thisBank = ctx.function.bank(ctx.options) + val targetBank = f.bank(ctx.options) + if (ctx.options.platform.isUnsafeToJump(targetBank, thisBank)) { + ctx.log.warn(s"Unsafe call to function `${f.name}` in segment `$targetBank` from function `${ctx.function.name}` in overlapping segment `$thisBank`", expr.position) + } + case _ => + } + } FunctionCallExpression(name, args.map(arg => optimizeExpr(arg, currentVarValues))).pos(pos) } case SumExpression(expressions, false) if optimizeSum =>