fix Zp allocation issues

This commit is contained in:
Irmen de Jong 2022-01-15 12:29:29 +01:00
parent 641477d6f6
commit 7dd2517f67
5 changed files with 58 additions and 70 deletions

View File

@ -1465,22 +1465,11 @@ $repeatLabel lda $counterVar
when(dt) { when(dt) {
DataType.UBYTE, DataType.UWORD -> { DataType.UBYTE, DataType.UWORD -> {
val result = zeropage.allocate(counterVar, dt, null, stmt.position, errors) val result = zeropage.allocate(counterVar, dt, null, stmt.position, errors)
return result.fold( result.fold(
success = { zpvar -> success = { zpvar -> asmInfo.extraVars.add(Triple(dt, counterVar, zpvar.first)) },
asmInfo.extraVars.add(Triple(dt, counterVar, zpvar.first)) failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
counterVar
},
failure = { zpError ->
if(mustBeInZeropage) {
errors.err(zpError.message!!, stmt.position)
null
} else {
// allocate normally
asmInfo.extraVars.add(Triple(dt, counterVar, null))
counterVar
}
}
) )
return counterVar
} }
else -> throw AssemblyError("invalidt dt") else -> throw AssemblyError("invalidt dt")
} }

View File

@ -1311,12 +1311,21 @@ internal class AstChecker(private val program: Program,
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteralValue) : Boolean { private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteralValue) : Boolean {
return if (targetDt == DataType.STR) { return if (targetDt == DataType.STR) {
if (value.value.length > 255) { when {
errors.err("string length must be 0-255", value.position) value.value.length > 255 -> {
false errors.err("string length must be 0-255", value.position)
false
}
value.value.isEmpty() -> {
val decl = value.parent as? VarDecl
if(decl!=null && (decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)) {
errors.err("string in Zeropage must be non-empty", value.position)
false
}
else true
}
else -> true
} }
else
true
} }
else false else false
} }

View File

@ -1,7 +1,6 @@
package prog8tests package prog8tests
import com.github.michaelbull.result.expect import com.github.michaelbull.result.expectError
import com.github.michaelbull.result.get
import com.github.michaelbull.result.getOrElse import com.github.michaelbull.result.getOrElse
import com.github.michaelbull.result.onFailure import com.github.michaelbull.result.onFailure
import io.kotest.assertions.fail import io.kotest.assertions.fail
@ -22,6 +21,7 @@ import prog8.codegen.target.c64.C64Zeropage
import prog8.codegen.target.cx16.CX16Zeropage import prog8.codegen.target.cx16.CX16Zeropage
import prog8.compilerinterface.* import prog8.compilerinterface.*
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
import java.lang.IllegalArgumentException
class TestAbstractZeropage: FunSpec({ class TestAbstractZeropage: FunSpec({
@ -100,21 +100,18 @@ class TestC64Zeropage: FunSpec({
result.onFailure { fail(it.toString()) } result.onFailure { fail(it.toString()) }
result = zp.allocate("varname", DataType.UBYTE, null, null, errors) result = zp.allocate("varname", DataType.UBYTE, null, null, errors)
result.onFailure { fail(it.toString()) } result.onFailure { fail(it.toString()) }
result = zp.allocate("varname", DataType.UBYTE, null, null, errors) shouldThrow<IllegalArgumentException> { zp.allocate("varname", DataType.UBYTE, null, null, errors) }
result.onFailure { fail(it.toString()) } // TODO SHOULD ACTUALLY BE ERROR
result = zp.allocate("varname2", DataType.UBYTE, null, null, errors) result = zp.allocate("varname2", DataType.UBYTE, null, null, errors)
result.onFailure { fail(it.toString()) } result.onFailure { fail(it.toString()) }
} }
test("testZpFloatEnable") { test("testZpFloatEnable") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
shouldThrow<InternalCompilerException> { var result = zp.allocate("", DataType.FLOAT, null, null, errors)
zp.allocate("", DataType.FLOAT, null, null, errors) result.expectError { "should be allocation error due to disabled floats" }
}
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target)) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
shouldThrow<InternalCompilerException> { result = zp2.allocate("", DataType.FLOAT, null, null, errors)
zp2.allocate("", DataType.FLOAT, null, null, errors) result.expectError { "should be allocation error due to disabled ZP use" }
}
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target)) val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
zp3.allocate("", DataType.FLOAT, null, null, errors) zp3.allocate("", DataType.FLOAT, null, null, errors)
} }
@ -138,9 +135,8 @@ class TestC64Zeropage: FunSpec({
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
println(zp.free) println(zp.free)
zp.availableBytes() shouldBe 0 zp.availableBytes() shouldBe 0
shouldThrow<InternalCompilerException> { val result = zp.allocate("", DataType.BYTE, null, null, errors)
zp.allocate("", DataType.BYTE, null, null, errors) result.expectError { "expected error due to disabled ZP use" }
}
} }
test("testFreeSpacesBytes") { test("testFreeSpacesBytes") {
@ -185,10 +181,8 @@ class TestC64Zeropage: FunSpec({
zp.hasByteAvailable() shouldBe true zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() shouldBe true zp.hasWordAvailable() shouldBe true
shouldThrow<ZeropageDepletedError> { var result = zp.allocate("", DataType.FLOAT, null, null, errors)
// in regular zp there aren't 5 sequential bytes free result.expectError { "expect allocation error: in regular zp there aren't 5 sequential bytes free" }
zp.allocate("", DataType.FLOAT, null, null, errors)
}
for (i in 0 until zp.availableBytes()) { for (i in 0 until zp.availableBytes()) {
val result = zp.allocate("", DataType.UBYTE, null, null, errors) val result = zp.allocate("", DataType.UBYTE, null, null, errors)
@ -197,12 +191,10 @@ class TestC64Zeropage: FunSpec({
zp.availableBytes() shouldBe 0 zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() shouldBe false zp.hasWordAvailable() shouldBe false
shouldThrow<ZeropageDepletedError> { result = zp.allocate("", DataType.UBYTE, null, null, errors)
zp.allocate("", DataType.UBYTE, null, null, errors) result.expectError { "expected allocation error" }
} result = zp.allocate("", DataType.UWORD, null, null, errors)
shouldThrow<ZeropageDepletedError> { result.expectError { "expected allocation error" }
zp.allocate("", DataType.UWORD, null, null, errors)
}
} }
test("testFullAllocation") { test("testFullAllocation") {
@ -210,7 +202,7 @@ class TestC64Zeropage: FunSpec({
zp.availableBytes() shouldBe 239 zp.availableBytes() shouldBe 239
zp.hasByteAvailable() shouldBe true zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() shouldBe true zp.hasWordAvailable() shouldBe true
val result = zp.allocate("", DataType.UWORD, null, null, errors) var result = zp.allocate("", DataType.UWORD, null, null, errors)
val loc = result.getOrElse { throw it } .first val loc = result.getOrElse { throw it } .first
loc shouldBeGreaterThan 3u loc shouldBeGreaterThan 3u
loc shouldNotBeIn zp.free loc shouldNotBeIn zp.free
@ -221,10 +213,9 @@ class TestC64Zeropage: FunSpec({
} }
zp.availableBytes() shouldBe 5 zp.availableBytes() shouldBe 5
shouldThrow<ZeropageDepletedError> { // can't allocate because no more sequential bytes, only fragmented
// can't allocate because no more sequential bytes, only fragmented result = zp.allocate("", DataType.UWORD, null, null, errors)
zp.allocate("", DataType.UWORD, null, null, errors) result.expectError { "should give allocation error" }
}
for(i in 0..4) { for(i in 0..4) {
zp.allocate("", DataType.UBYTE, null, null, errors) zp.allocate("", DataType.UBYTE, null, null, errors)
@ -233,27 +224,25 @@ class TestC64Zeropage: FunSpec({
zp.availableBytes() shouldBe 0 zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() shouldBe false zp.hasWordAvailable() shouldBe false
shouldThrow<ZeropageDepletedError> { result = zp.allocate("", DataType.UBYTE, null, null, errors)
// no more space result.expectError { "should give allocation error" }
zp.allocate("", DataType.UBYTE, null, null, errors)
}
} }
test("testEfficientAllocation") { test("testEfficientAllocation") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
zp.availableBytes() shouldBe 18 zp.availableBytes() shouldBe 18
zp.allocate("", DataType.WORD, null, null, errors) shouldBe 0x04u zp.allocate("", DataType.WORD, null, null, errors ).getOrElse{throw it}.first shouldBe 0x04u
zp.allocate("", DataType.UBYTE, null, null, errors) shouldBe 0x06u zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x06u
zp.allocate("", DataType.UBYTE, null, null, errors) shouldBe 0x0au zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x0au
zp.allocate("", DataType.UWORD, null, null, errors) shouldBe 0x9bu zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x9bu
zp.allocate("", DataType.UWORD, null, null, errors) shouldBe 0x9eu zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x9eu
zp.allocate("", DataType.UWORD, null, null, errors) shouldBe 0xa5u zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xa5u
zp.allocate("", DataType.UWORD, null, null, errors) shouldBe 0xb0u zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xb0u
zp.allocate("", DataType.UWORD, null, null, errors) shouldBe 0xbeu zp.allocate("", DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xbeu
zp.allocate("", DataType.UBYTE, null, null, errors) shouldBe 0x0eu zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x0eu
zp.allocate("", DataType.UBYTE, null, null, errors) shouldBe 0x92u zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x92u
zp.allocate("", DataType.UBYTE, null, null, errors) shouldBe 0x96u zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x96u
zp.allocate("", DataType.UBYTE, null, null, errors) shouldBe 0xf9u zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0xf9u
zp.availableBytes() shouldBe 0 zp.availableBytes() shouldBe 0
} }

View File

@ -6,7 +6,7 @@ import com.github.michaelbull.result.Result
import prog8.ast.base.* import prog8.ast.base.*
class ZeropageDepletedError(message: String) : Exception(message) class ZeropageAllocationError(message: String) : Exception(message)
abstract class Zeropage(protected val options: CompilationOptions) { abstract class Zeropage(protected val options: CompilationOptions) {
@ -36,11 +36,11 @@ abstract class Zeropage(protected val options: CompilationOptions) {
return free.windowed(2).any { it[0] == it[1] - 1u } return free.windowed(2).any { it[0] == it[1] - 1u }
} }
fun allocate(scopedname: String, datatype: DataType, arraySize: Int?, position: Position?, errors: IErrorReporter): Result<Pair<UInt, Int>, ZeropageDepletedError> { fun allocate(scopedname: String, datatype: DataType, arraySize: Int?, position: Position?, errors: IErrorReporter): Result<Pair<UInt, Int>, ZeropageAllocationError> {
require(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"} require(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
if(options.zeropage== ZeropageType.DONTUSE) if(options.zeropage== ZeropageType.DONTUSE)
throw InternalCompilerException("zero page usage has been disabled") return Err(ZeropageAllocationError("zero page usage has been disabled"))
val size: Int = val size: Int =
when (datatype) { when (datatype) {
@ -61,9 +61,9 @@ abstract class Zeropage(protected val options: CompilationOptions) {
else else
errors.warn("$scopedname: allocating a large value in zeropage; float $memsize bytes", Position.DUMMY) errors.warn("$scopedname: allocating a large value in zeropage; float $memsize bytes", Position.DUMMY)
memsize memsize
} else throw InternalCompilerException("floating point option not enabled") } else return Err(ZeropageAllocationError("floating point option not enabled"))
} }
else -> throw InternalCompilerException("cannot put datatype $datatype in zeropage") else -> return Err(ZeropageAllocationError("cannot put datatype $datatype in zeropage"))
} }
synchronized(this) { synchronized(this) {
@ -82,7 +82,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
} }
} }
return Err(ZeropageDepletedError("no more free space in ZP to allocate $size sequential bytes")) return Err(ZeropageAllocationError("no more free space in ZP to allocate $size sequential bytes"))
} }
private fun reserve(range: UIntRange) = free.removeAll(range) private fun reserve(range: UIntRange) = free.removeAll(range)

View File

@ -6,6 +6,7 @@ For next compiler release (7.7)
- fix array and string initialization in zeropage - fix array and string initialization in zeropage
- fix cx16 zeropage preallocated vars (virtual regs) - fix cx16 zeropage preallocated vars (virtual regs)
- fix ForloopAsmGen zp allocation handling - fix ForloopAsmGen zp allocation handling
- check all examples if they still run correctly (c64 + cx16)
- document check: arrays and strings can also be placed in zeropage (but almost never should, due to size!) - document check: arrays and strings can also be placed in zeropage (but almost never should, due to size!)
- document @requirezp and add to syntax def files and IDEA - document @requirezp and add to syntax def files and IDEA