mirror of
https://github.com/irmen/prog8.git
synced 2025-02-09 07:31:34 +00:00
fix Zp allocation issues
This commit is contained in:
parent
641477d6f6
commit
7dd2517f67
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user