mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-10 01:36:59 +00:00
Never remove or inline volatile variables (fixes #27)
This commit is contained in:
parent
b9cdd0ffff
commit
6e36166af2
@ -4,7 +4,7 @@
|
||||
|
||||
Variables in Millfork can belong to one of the following storage classes:
|
||||
|
||||
* static: all global variables; local variables declared with `static`
|
||||
* static: all global variables; local variables declared with `static` or `volatile`
|
||||
|
||||
* stack: local variables declared with `stack`
|
||||
|
||||
|
@ -43,7 +43,8 @@ Examples:
|
||||
|
||||
* `volatile` means that the variable is volatile.
|
||||
The optimizer shouldn't remove or reorder accesses to volatile variables.
|
||||
Volatile variables cannot be declared as `register` or `stack.
|
||||
Volatile variables (unline non-volatile ones) will not be removed or inlined by the optimizer.
|
||||
Volatile variables cannot be declared as `register` or `stack`.
|
||||
|
||||
* `<storage>` can be only specified for local variables. It can be either `stack`, `static`, `register` or nothing.
|
||||
`register` is only a hint for the optimizer.
|
||||
|
@ -79,13 +79,13 @@ object TwoVariablesToIndexRegistersOptimization extends AssemblyOptimization[Ass
|
||||
case _ => None
|
||||
}.toSet
|
||||
val localVariables = f.environment.getAllLocalVariables.filter {
|
||||
case MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name)
|
||||
case v@MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile
|
||||
case _ => false
|
||||
}
|
||||
val variablesWithRegisterHint = f.environment.getAllLocalVariables.filter {
|
||||
case MemoryVariable(name, typ, VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name)
|
||||
case v@MemoryVariable(name, typ, VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile
|
||||
case _ => false
|
||||
}.map(_.name).toSet
|
||||
|
||||
|
@ -156,13 +156,13 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
||||
case _ => None
|
||||
}.toSet
|
||||
val localVariables = f.environment.getAllLocalVariables.filter {
|
||||
case MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name)
|
||||
case v@MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile
|
||||
case _ => false
|
||||
}
|
||||
val variablesWithRegisterHint = f.environment.getAllLocalVariables.filter {
|
||||
case MemoryVariable(name, typ, VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name)
|
||||
case v@MemoryVariable(name, typ, VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile
|
||||
case _ => false
|
||||
}.map(_.name).toSet
|
||||
|
||||
|
@ -210,7 +210,7 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
|
||||
var bonus = CyclesAndBytes.Zero
|
||||
if (vs.variablesWithRegisterHint(v.name)) bonus += CyclesAndBytes(16, 16)
|
||||
if (id.startsWith("IX+") || id.startsWith("IY+")) bonus += savingsForRemovingOneStackVariable
|
||||
if (id.startsWith("SP+")) None
|
||||
if (id.startsWith("SP+") || v.isVolatile) None
|
||||
else canBeInlined(id, synced = false, register, Some(false), Some(false), Some(false), vs.codeWithFlow.slice(range.start, range.end)).map { score =>
|
||||
(id, range, score + bonus)
|
||||
}
|
||||
|
@ -223,6 +223,7 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
|
||||
|
||||
def okPrefix(vs: VariableStatus, v: Variable, range: Range, reg: ZRegister.Value, allowDirectLoad: Boolean, allowIndirectLoad: Boolean): Boolean = {
|
||||
if (!vs.paramVariables(v.name)) return true
|
||||
if (v.isVolatile) return false
|
||||
if (!allowDirectLoad && !allowIndirectLoad) {
|
||||
// println(s"okPrefix $v false: better cpu required for $reg")
|
||||
return false
|
||||
|
@ -1742,6 +1742,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
else if (stmt.global) VariableAllocationMethod.Static
|
||||
else if (stmt.register) VariableAllocationMethod.Register
|
||||
else VariableAllocationMethod.Auto
|
||||
if (stmt.volatile && !stmt.global) {
|
||||
log.warn(s"Volatile variable `$name` assumed to be static", position)
|
||||
}
|
||||
if (stmt.volatile && stmt.stack) {
|
||||
log.error(s"Volatile variable `$name` cannot be allocated on stack", position)
|
||||
}
|
||||
if (stmt.volatile && stmt.register) {
|
||||
log.error(s"Volatile variable `$name` cannot be allocated in a register", position)
|
||||
}
|
||||
if (alloc != VariableAllocationMethod.Static && stmt.initialValue.isDefined) {
|
||||
log.error(s"`$name` cannot be preinitialized`", position)
|
||||
}
|
||||
|
@ -15,9 +15,8 @@ object UnusedGlobalVariables extends NodeOptimization {
|
||||
case AliasDefinitionStatement(source, target, _) => Some(source -> target)
|
||||
case _ => None
|
||||
}.toMap
|
||||
// TODO: volatile
|
||||
val allNonvolatileGlobalVariables = nodes.flatMap {
|
||||
case v: VariableDeclarationStatement => if (v.address.isDefined) Nil else List(v.name)
|
||||
case v: VariableDeclarationStatement => if (v.address.isDefined || v.volatile) Nil else List(v.name)
|
||||
case v: ArrayDeclarationStatement => if (v.address.isDefined) Nil else List(v.name)
|
||||
case _ => Nil
|
||||
}.toSet
|
||||
|
@ -19,12 +19,12 @@ object UnusedLocalVariables extends NodeOptimization {
|
||||
Nil
|
||||
}
|
||||
|
||||
def getAllLocalVariables(statements: List[Statement]): List[String] = statements.flatMap {
|
||||
case v: VariableDeclarationStatement => List(v.name)
|
||||
def getAllRemovableLocalVariables(statements: List[Statement]): List[String] = statements.flatMap {
|
||||
case v: VariableDeclarationStatement => if (v.volatile) Nil else List(v.name)
|
||||
case v: ArrayDeclarationStatement => List(v.name)
|
||||
case x: IfStatement => getAllLocalVariables(x.thenBranch) ++ getAllLocalVariables(x.elseBranch)
|
||||
case x: WhileStatement => getAllLocalVariables(x.body)
|
||||
case x: DoWhileStatement => getAllLocalVariables(x.body)
|
||||
case x: IfStatement => getAllRemovableLocalVariables(x.thenBranch) ++ getAllRemovableLocalVariables(x.elseBranch)
|
||||
case x: WhileStatement => getAllRemovableLocalVariables(x.body)
|
||||
case x: DoWhileStatement => getAllRemovableLocalVariables(x.body)
|
||||
case _ => Nil
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ object UnusedLocalVariables extends NodeOptimization {
|
||||
}
|
||||
|
||||
def optimizeVariables(log: Logger, statements: List[Statement]): List[Statement] = {
|
||||
val allLocals = getAllLocalVariables(statements)
|
||||
val allLocals = getAllRemovableLocalVariables(statements)
|
||||
val allRead = statements.flatMap {
|
||||
case Assignment(VariableExpression(v), expression) => List(extractThingName(v) -> expression)
|
||||
case ExpressionStatement(FunctionCallExpression(op, VariableExpression(_) :: params)) if op.endsWith("=") => params.map("```" -> _)
|
||||
|
@ -10,14 +10,18 @@ import org.scalatest.{FunSuite, Matchers}
|
||||
class VolatileSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Basic volatile test") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086)(
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086, Cpu.Motorola6809)(
|
||||
"""
|
||||
| word addr @$c000
|
||||
| volatile byte output @$c0ea
|
||||
| volatile byte unused_but_should_exist
|
||||
| byte unused_global
|
||||
| byte thing
|
||||
| void main () {
|
||||
| static volatile byte unused_local
|
||||
| f(55)
|
||||
| addr = f.addr
|
||||
| unused_local = 55
|
||||
| }
|
||||
| noinline void f(byte x) {
|
||||
| output = 5
|
||||
|
Loading…
x
Reference in New Issue
Block a user