From 0a36d83164f007616a454b3b79a3186eb48bd6e1 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Tue, 16 Apr 2019 16:56:23 +0200 Subject: [PATCH] nullptr --- CHANGELOG.md | 6 ++++- docs/lang/preprocessor.md | 2 ++ docs/lang/types.md | 9 ++++++- src/main/scala/millfork/Main.scala | 4 +-- .../compiler/AbstractExpressionCompiler.scala | 2 +- .../compiler/CompilationContext.scala | 2 +- src/main/scala/millfork/env/Environment.scala | 25 +++++++++++++++---- src/main/scala/millfork/env/Thing.scala | 14 +++++++++++ .../scala/millfork/test/PointerSuite.scala | 18 ++++++++++++- .../scala/millfork/test/ZLineSizeSuite.scala | 2 +- src/test/scala/millfork/test/emu/EmuRun.scala | 4 +-- .../scala/millfork/test/emu/EmuZ80Run.scala | 4 +-- .../millfork/test/emu/ShouldNotCompile.scala | 2 +- 13 files changed, 76 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc9d9e8..f82034b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ * Pointers can now be typed. +* Added `nullptr`. + * Arrays can now have elements of types other than `byte` (still limited in size to 1 byte though). * Added hint for identifiers with typos. @@ -36,7 +38,9 @@ * Fixed `#use` not accessing all preprocessor parameters. -* Fixed `#pragma` not respecting `#if` +* Fixed `#pragma` not respecting `#if`. + +* 8080 and LR35902: fixed large stack variables. * Optimization improvements. diff --git a/docs/lang/preprocessor.md b/docs/lang/preprocessor.md index cb91b5df..a1803ad9 100644 --- a/docs/lang/preprocessor.md +++ b/docs/lang/preprocessor.md @@ -90,6 +90,8 @@ The following features are defined based on the chosen CPU and compilation optio * `PAL` – 1 if the target is PAL, 0 otherwise +* `NULLPTR` – physical value of `nullptr`, default 0 + ### Built-in preprocessor functions and operators The `defined` function returns 1 if the feature is defined, 0 otherwise. diff --git a/docs/lang/types.md b/docs/lang/types.md index 495208a2..db20f2b9 100644 --- a/docs/lang/types.md +++ b/docs/lang/types.md @@ -66,6 +66,13 @@ Examples: p[i] // valid only if the type 't' is of size 1, equivalent to 't(p.raw[i])' p->x // valid only if the type 't' has a field called 'x', accesses the field 'x' of the pointed element p->x.y->z // you can stack it + +## `nullptr` + +There is a 2-byte constant `nullptr` that can be assigned to any 2-byte pointer type. +Its actual value is defined using the feature `NULLPTR`, by default it's 0. + +`nullptr` isn't directly assignable to non-pointer types. ## Boolean types @@ -103,7 +110,7 @@ Assigment between numeric types and enumerations is not possible without an expl Plain enumerations have their variants equal to `byte(0)` to `byte(.count - 1)`. Tip: You can use an enumeration with no variants as a strongly checked alternative byte type, -as there are no checks no values when converting bytes to enumeration values and vice versa. +as there are no checks on values when converting bytes to enumeration values and vice versa. ## Structs diff --git a/src/main/scala/millfork/Main.scala b/src/main/scala/millfork/Main.scala index 5d4074f9..7fd6ed44 100644 --- a/src/main/scala/millfork/Main.scala +++ b/src/main/scala/millfork/Main.scala @@ -196,7 +196,7 @@ object Main { } val callGraph = new StandardCallGraph(program, options.log) - val env = new Environment(None, "", platform.cpuFamily, options.jobContext) + val env = new Environment(None, "", platform.cpuFamily, options) env.collectDeclarations(program, options) val assemblyOptimizations = optLevel match { @@ -248,7 +248,7 @@ object Main { } val callGraph = new StandardCallGraph(program, options.log) - val env = new Environment(None, "", platform.cpuFamily, options.jobContext) + val env = new Environment(None, "", platform.cpuFamily, options) env.collectDeclarations(program, options) val assemblyOptimizations = optLevel match { diff --git a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala index 4fd43698..d5d5b321 100644 --- a/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/AbstractExpressionCompiler.scala @@ -28,7 +28,7 @@ class AbstractExpressionCompiler[T <: AbstractCode] { } def callingContext(ctx: CompilationContext, callee: String, v: VariableInMemory): CompilationContext = { - val result = new Environment(Some(ctx.env), "", ctx.options.platform.cpuFamily, ctx.jobContext) + val result = new Environment(Some(ctx.env), "", ctx.options.platform.cpuFamily, ctx.options) val localName = v.name.stripPrefix(callee + '$') result.addVariable(ctx.options, localName, v, None) ctx.copy(env = result) diff --git a/src/main/scala/millfork/compiler/CompilationContext.scala b/src/main/scala/millfork/compiler/CompilationContext.scala index c5c88cf6..53907630 100644 --- a/src/main/scala/millfork/compiler/CompilationContext.scala +++ b/src/main/scala/millfork/compiler/CompilationContext.scala @@ -16,7 +16,7 @@ case class CompilationContext(env: Environment, breakLabels: Map[String, Label] = Map(), continueLabels: Map[String, Label] = Map()){ def withInlinedEnv(environment: Environment, newLabel: String): CompilationContext = { - val newEnv = new Environment(Some(env), newLabel, environment.cpuFamily, jobContext) + val newEnv = new Environment(Some(env), newLabel, environment.cpuFamily, options) newEnv.things ++= environment.things copy(env = newEnv) } diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index e80dbb43..1bf019c7 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -18,8 +18,10 @@ import scala.collection.mutable.ListBuffer * @author Karol Stasiak */ //noinspection NotImplementedCode -class Environment(val parent: Option[Environment], val prefix: String, val cpuFamily: CpuFamily.Value, val jobContext: JobContext) { +class Environment(val parent: Option[Environment], val prefix: String, val cpuFamily: CpuFamily.Value, val options: CompilationOptions) { + @inline + def jobContext: JobContext = options.jobContext @inline def log: Logger = jobContext.log @inline @@ -50,7 +52,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa m.environment.getAllPrefixedThings case _ => Map[String, Thing]() }.fold(things.toMap)(_ ++ _) - val e = new Environment(None, "", cpuFamily, jobContext) + val e = new Environment(None, "", cpuFamily, options) e.things.clear() e.things ++= allThings e @@ -397,6 +399,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa if (parent.isEmpty) { addThing(VoidType, None) + addThing(NullType, None) addThing(BuiltInBooleanType, None) val b = BasicPlainType("byte", 1) val w = BasicPlainType("word", 2) @@ -433,8 +436,20 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa addThing(falseType, None) addThing(ConstantThing("true", NumericConstant(1, 0), trueType), None) addThing(ConstantThing("false", NumericConstant(0, 0), falseType), None) - addThing(ConstantThing("__zeropage_usage", UnexpandedConstant("__zeropage_usage", 1), b), None) - addThing(ConstantThing("__heap_start", UnexpandedConstant("__heap_start", 2), p), None) + val nullptrValue = options.features.getOrElse("NULLPTR", 0L) + val nullptrConstant = NumericConstant(nullptrValue, 2) + addThing(ConstantThing("nullptr", nullptrConstant, NullType), None) + addThing(ConstantThing("nullptr.hi", nullptrConstant.hiByte.quickSimplify, b), None) + addThing(ConstantThing("nullptr.lo", nullptrConstant.loByte.quickSimplify, b), None) + addThing(ConstantThing("nullptr.raw", nullptrConstant, p), None) + addThing(ConstantThing("nullptr.raw.hi", nullptrConstant.hiByte.quickSimplify, b), None) + addThing(ConstantThing("nullptr.raw.lo", nullptrConstant.loByte.quickSimplify, b), None) + val __zeropage_usage = UnexpandedConstant("__zeropage_usage", 1) + addThing(ConstantThing("__zeropage_usage", __zeropage_usage, b), None) + val __heap_start = UnexpandedConstant("__heap_start", 2) + addThing(ConstantThing("__heap_start", __heap_start, p), None) + addThing(ConstantThing("__heap_start.hi", __heap_start.hiByte, b), None) + addThing(ConstantThing("__heap_start.lo", __heap_start.loByte, b), None) addThing(ConstantThing("$0000", NumericConstant(0, 2), p), None) addThing(FlagBooleanType("set_carry", BranchingOpcodeMapping(Opcode.BCS, IfFlagSet(ZFlag.C)), @@ -928,7 +943,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa log.error(s"Non-macro function `$name` cannot have inlinable parameters", stmt.position) } - val env = new Environment(Some(this), name + "$", cpuFamily, jobContext) + val env = new Environment(Some(this), name + "$", cpuFamily, options) stmt.params.foreach(p => env.registerParameter(p, options)) def params: ParamSignature = if (stmt.assembly) { AssemblyParamSignature(stmt.params.map { diff --git a/src/main/scala/millfork/env/Thing.scala b/src/main/scala/millfork/env/Thing.scala index dff3b221..73cb9f6a 100644 --- a/src/main/scala/millfork/env/Thing.scala +++ b/src/main/scala/millfork/env/Thing.scala @@ -82,6 +82,20 @@ case class PointerType(name: String, targetName: String, var target: Option[Type override def pointerTargetName: String = targetName } +case object NullType extends VariableType { + override def size: Int = 2 + + override def isSigned: Boolean = false + + override def name: String = "null$" + + override def isPointy: Boolean = true + + override def isSubtypeOf(other: Type): Boolean = this == other || other.isPointy && other.size == 2 + + override def isAssignableTo(targetType: Type): Boolean = this == targetType || targetType.isPointy && targetType.size == 2 +} + case class EnumType(name: String, count: Option[Int]) extends VariableType { override def size: Int = 1 diff --git a/src/test/scala/millfork/test/PointerSuite.scala b/src/test/scala/millfork/test/PointerSuite.scala index 12b53eec..39bb5504 100644 --- a/src/test/scala/millfork/test/PointerSuite.scala +++ b/src/test/scala/millfork/test/PointerSuite.scala @@ -102,7 +102,7 @@ class PointerSuite extends FunSuite with Matchers { | | void main() { | heapEnd = heap.addr - | this = pointer.pointlist(0) + | this = nullptr | point tmp | tmp.x = 3 | tmp.y = 3 @@ -152,4 +152,20 @@ class PointerSuite extends FunSuite with Matchers { m.readByte(0xc000) should equal(44) } } + + test("nullptr") { + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)( + """ + | void main() { + | pointer.word pw + | pointer p + | word w + | pw = nullptr + | p = nullptr + | w = word(nullptr) + | } + """.stripMargin) { m => + + } + } } diff --git a/src/test/scala/millfork/test/ZLineSizeSuite.scala b/src/test/scala/millfork/test/ZLineSizeSuite.scala index 5fa9eb3f..3ccf2c09 100644 --- a/src/test/scala/millfork/test/ZLineSizeSuite.scala +++ b/src/test/scala/millfork/test/ZLineSizeSuite.scala @@ -17,8 +17,8 @@ class ZLineSizeSuite extends FunSuite with Matchers { private def runCase(line: ZLine): Unit = { val platform = EmuPlatform.get(Cpu.Z80) val jobContext = JobContext(TestErrorReporting.log, new LabelGenerator) - val env = new Environment(None, "", CpuFamily.I80, jobContext) val options = CompilationOptions(platform, Map(), None, 0, Map(), jobContext) + val env = new Environment(None, "", CpuFamily.I80, options) val correctSize = new Z80Assembler(null, env, platform).emitInstruction("default", options, 0x100, line) - 0x100 val guessedSize = line.sizeInBytes guessedSize should equal(correctSize) diff --git a/src/test/scala/millfork/test/emu/EmuRun.scala b/src/test/scala/millfork/test/emu/EmuRun.scala index 8663e9f7..c0bb3a32 100644 --- a/src/test/scala/millfork/test/emu/EmuRun.scala +++ b/src/test/scala/millfork/test/emu/EmuRun.scala @@ -185,7 +185,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], } val program = nodeOptimizations.foldLeft(withLibraries.applyImportantAliases)((p, opt) => p.applyNodeOptimization(opt, options)) val callGraph = new StandardCallGraph(program, log) - val env = new Environment(None, "", CpuFamily.M6502, options.jobContext) + val env = new Environment(None, "", CpuFamily.M6502, options) env.collectDeclarations(program, options) val hasOptimizations = assemblyOptimizations.nonEmpty @@ -205,7 +205,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], // compile - val env2 = new Environment(None, "", CpuFamily.M6502, options.jobContext) + val env2 = new Environment(None, "", CpuFamily.M6502, options) env2.collectDeclarations(program, options) val assembler = new MosAssembler(program, env2, platform) val output = assembler.assemble(callGraph, assemblyOptimizations, options) diff --git a/src/test/scala/millfork/test/emu/EmuZ80Run.scala b/src/test/scala/millfork/test/emu/EmuZ80Run.scala index 04785735..927b22a5 100644 --- a/src/test/scala/millfork/test/emu/EmuZ80Run.scala +++ b/src/test/scala/millfork/test/emu/EmuZ80Run.scala @@ -109,7 +109,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio } val program = nodeOptimizations.foldLeft(withLibraries.applyImportantAliases)((p, opt) => p.applyNodeOptimization(opt, options)) val callGraph = new StandardCallGraph(program, log) - val env = new Environment(None, "", CpuFamily.I80, options.jobContext) + val env = new Environment(None, "", CpuFamily.I80, options) env.collectDeclarations(program, options) val hasOptimizations = assemblyOptimizations.nonEmpty @@ -129,7 +129,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio // compile - val env2 = new Environment(None, "", CpuFamily.I80, options.jobContext) + val env2 = new Environment(None, "", CpuFamily.I80, options) env2.collectDeclarations(program, options) val assembler = new Z80Assembler(program, env2, platform) val output = assembler.assemble(callGraph, assemblyOptimizations, options) diff --git a/src/test/scala/millfork/test/emu/ShouldNotCompile.scala b/src/test/scala/millfork/test/emu/ShouldNotCompile.scala index b81d9ff0..a0e1e3d8 100644 --- a/src/test/scala/millfork/test/emu/ShouldNotCompile.scala +++ b/src/test/scala/millfork/test/emu/ShouldNotCompile.scala @@ -45,7 +45,7 @@ object ShouldNotCompile extends Matchers { // prepare val callGraph = new StandardCallGraph(program, log) val cpuFamily = CpuFamily.forType(cpu) - val env = new Environment(None, "", cpuFamily, options.jobContext) + val env = new Environment(None, "", cpuFamily, options) env.collectDeclarations(program, options) var unoptimizedSize = 0L