more tests and some cleanups/fixes

This commit is contained in:
Irmen de Jong 2024-09-08 18:35:40 +02:00
parent 759babb4c1
commit 8f5d42dbc2
13 changed files with 147 additions and 87 deletions

View File

@ -55,19 +55,17 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
}
}
infix fun isSameAs(target: PtAssignTarget): Boolean {
return when {
target.memory != null && this is PtMemoryByte-> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
infix fun isSameAs(target: PtAssignTarget): Boolean = when {
target.memory != null && this is PtMemoryByte -> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
}
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()

View File

@ -149,7 +149,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
else {
// normal array to array copy, various element types
val eltsize = asmgen.options.compTarget.memorySize(source.type.sub!!) // TODO test this!
val eltsize = asmgen.options.compTarget.memorySize(source.type.sub!!)
val numBytes = numElements * eltsize
asmgen.out("""
lda #<${sourceAsm}

View File

@ -604,19 +604,20 @@ internal class ProgramAndVarsGen(
}
private fun uninitializedVariable2asm(variable: StStaticVariable) {
val dt = variable.dt
when {
variable.dt.isBool || variable.dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ?")
variable.dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
variable.dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
variable.dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
variable.dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
variable.dt.isSplitWordArray -> {
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ?")
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
dt.isSplitWordArray -> {
val numbytesPerHalf = compTarget.memorySize(dt, variable.length!!) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
}
variable.dt.isArray -> {
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
dt.isArray -> {
val numbytes = compTarget.memorySize(dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes")
}
else -> {
@ -634,12 +635,13 @@ internal class ProgramAndVarsGen(
variable.onetimeInitializationNumericValue!!.toInt()
} else 0
val dt=variable.dt
when {
variable.dt.isBool || variable.dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
variable.dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
variable.dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
variable.dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
variable.dt.isFloat -> {
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
dt.isFloat -> {
if(initialValue==0) {
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
} else {
@ -647,10 +649,10 @@ internal class ProgramAndVarsGen(
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
}
}
variable.dt.isString -> {
dt.isString -> {
throw AssemblyError("all string vars should have been interned into prog")
}
variable.dt.isArray -> arrayVariable2asm(variable.name, variable.dt, variable.onetimeInitializationArrayValue, variable.length)
dt.isArray -> arrayVariable2asm(variable.name, dt, variable.onetimeInitializationArrayValue, variable.length)
else -> {
throw AssemblyError("weird dt")
}

View File

@ -98,7 +98,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
else {
// normal array to array copy (various element types)
val eltsize = codeGen.options.compTarget.memorySize(source.type.sub!!) // TODO test this!
val eltsize = codeGen.options.compTarget.memorySize(source.type.sub!!)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
@ -583,8 +583,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if(arr!=null && index!=null) {
if(arr.splitWords) TODO("IR rol/ror on split words array")
val variable = arr.variable.name
val itemsize = codeGen.program.memsizer.memorySize(arr.type.sub!!) // TODO test this!
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null)
val offset = codeGen.program.memsizer.memorySize(arr.type, index)
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = offset), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
@ -669,7 +669,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
else {
val eltSize = codeGen.program.memsizer.memorySize(target.type.sub!!) // TODO test this!
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
val constIndex = target.index.asConstInteger()
if(isConstZeroValue) {
if(constIndex!=null) {

View File

@ -317,18 +317,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, tr, tr.resultReg, tr.resultFpReg)
var actualResultReg2 = -1
var actualResultFpReg2 = -1
val valueDt = cast.value.type
when(cast.type.dt) {
BaseDataType.BOOL -> {
when {
cast.value.type.isByte -> {
valueDt.isByte -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg), null)
}
cast.value.type.isWord -> {
valueDt.isWord -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg), null)
}
cast.value.type.isFloat -> {
valueDt.isFloat -> {
actualResultReg2 = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SGN, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg)
@ -339,7 +340,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
BaseDataType.UBYTE -> {
when(cast.value.type.dt) {
when(valueDt.dt) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg // just keep the LSB as it is
}
@ -351,7 +352,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
BaseDataType.BYTE -> {
when(cast.value.type.dt) {
when(valueDt.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg // just keep the LSB as it is
}
@ -363,7 +364,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
BaseDataType.UWORD -> {
when(cast.value.type.dt) {
when(valueDt.dt) {
BaseDataType.BYTE -> {
// byte -> uword: sign extend
actualResultReg2 = codeGen.registers.nextFree()
@ -385,7 +386,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
BaseDataType.WORD -> {
when(cast.value.type.dt) {
when(valueDt.dt) {
BaseDataType.BYTE -> {
// byte -> word: sign extend
actualResultReg2 = codeGen.registers.nextFree()
@ -408,7 +409,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
BaseDataType.FLOAT -> {
actualResultFpReg2 = codeGen.registers.nextFreeFloat()
when(cast.value.type.dt) {
when(valueDt.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE -> {
addInstr(result, IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}

View File

@ -400,8 +400,9 @@ internal class ConstantIdentifierReplacer(
// convert the initializer range expression from a range or int, to an actual array.
// this is to allow initialization of arrays with a single value like ubyte[10] array = 42
val dt = decl.datatype
when {
decl.datatype.isUnsignedByteArray || decl.datatype.isSignedByteArray || decl.datatype.isUnsignedWordArray || decl.datatype.isSignedWordArray -> {
dt.isUnsignedByteArray || dt.isSignedByteArray || dt.isUnsignedWordArray || dt.isSignedWordArray -> {
val rangeExpr = decl.value as? RangeExpression
if(rangeExpr!=null) {
val constRange = rangeExpr.toConstantIntegerRange()
@ -417,12 +418,12 @@ internal class ConstantIdentifierReplacer(
if(constRange!=null) {
val rangeType = rangeExpr.inferType(program).getOr(DataType.forDt(BaseDataType.UBYTE))
return if(rangeType.isByte) {
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
ArrayLiteral(InferredTypes.InferredType.known(dt),
constRange.map { NumericLiteral(rangeType.dt, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
} else {
require(rangeType.sub!=null)
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
ArrayLiteral(InferredTypes.InferredType.known(dt),
constRange.map { NumericLiteral(rangeType.sub!!.dt, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}
@ -436,30 +437,30 @@ internal class ConstantIdentifierReplacer(
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
val fillvalue = numericLv.number.toInt()
when {
decl.datatype.isUnsignedByteArray -> {
dt.isUnsignedByteArray -> {
if(fillvalue !in 0..255)
errors.err("ubyte value overflow", numericLv.position)
}
decl.datatype.isSignedByteArray -> {
dt.isSignedByteArray -> {
if(fillvalue !in -128..127)
errors.err("byte value overflow", numericLv.position)
}
decl.datatype.isUnsignedWordArray -> {
dt.isUnsignedWordArray -> {
if(fillvalue !in 0..65535)
errors.err("uword value overflow", numericLv.position)
}
decl.datatype.isSignedWordArray -> {
dt.isSignedWordArray -> {
if(fillvalue !in -32768..32767)
errors.err("word value overflow", numericLv.position)
}
else -> {}
}
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteral(decl.datatype.elementType().dt, it.toDouble(), numericLv.position) }.toTypedArray<Expression>()
return ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
val array = Array(size) {fillvalue}.map { NumericLiteral(dt.elementType().dt, it.toDouble(), numericLv.position) }.toTypedArray<Expression>()
return ArrayLiteral(InferredTypes.InferredType.known(dt), array, position = numericLv.position)
}
}
decl.datatype.isFloatArray -> {
dt.isFloatArray -> {
val rangeExpr = decl.value as? RangeExpression
if(rangeExpr!=null) {
// convert the initializer range expression to an actual array of floats
@ -487,7 +488,7 @@ internal class ConstantIdentifierReplacer(
}
}
}
decl.datatype.isBoolArray -> {
dt.isBoolArray -> {
val size = decl.arraysize?.constIndex() ?: return null
val numericLv = decl.value as? NumericLiteral
if(numericLv!=null) {

View File

@ -742,17 +742,18 @@ internal class AstChecker(private val program: Program,
val arraysize = decl.arraysize
if(arraysize!=null) {
val arraySize = arraysize.constIndex() ?: 1
val dt = decl.datatype
when {
decl.datatype.isString || decl.datatype.isByteArray || decl.datatype.isBoolArray ->
dt.isString || dt.isByteArray || dt.isBoolArray ->
if(arraySize > 256)
err("byte array length must be 1-256")
decl.datatype.isSplitWordArray ->
dt.isSplitWordArray ->
if(arraySize > 256)
err("split word array length must be 1-256")
decl.datatype.isWordArray ->
dt.isWordArray ->
if(arraySize > 128)
err("word array length must be 1-128")
decl.datatype.isFloatArray ->
dt.isFloatArray ->
if(arraySize > 51)
err("float array length must be 1-51")
else -> {}

View File

@ -137,16 +137,16 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
if(isMultiComparisonRecurse(leftBinExpr1)) {
val elementType = needle.inferType(program).getOrElse { throw FatalAstException("invalid needle dt") }
require(elementType.isNumericOrBool)
if(values.size==2 || values.size==3 && elementType in IntegerDatatypes) {
if(values.size==2 || values.size==3 && elementType.isInteger) {
val numbers = values.map{it.number}.toSet()
if(numbers == setOf(0.0, 1.0)) {
// we can replace x==0 or x==1 with x<2
val compare = BinaryExpression(needle, "<", NumericLiteral(elementType, 2.0, expr.position), expr.position)
val compare = BinaryExpression(needle, "<", NumericLiteral(elementType.dt, 2.0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNode(expr, compare, parent))
}
if(numbers == setOf(0.0, 1.0, 2.0)) {
// we can replace x==0 or x==1 or x==2 with x<3
val compare = BinaryExpression(needle, "<", NumericLiteral(elementType, 3.0, expr.position), expr.position)
val compare = BinaryExpression(needle, "<", NumericLiteral(elementType.dt, 3.0, expr.position), expr.position)
return listOf(IAstModification.ReplaceNode(expr, compare, parent))
}
}

View File

@ -10,6 +10,8 @@ import io.kotest.matchers.types.instanceOf
import prog8.ast.IFunctionCall
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.ast.PtArray
import prog8.code.ast.PtVariable
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.Position
@ -692,5 +694,36 @@ main {
DataType.arrayFor(BaseDataType.UWORD, true).isArray shouldBe true
DataType.arrayFor(BaseDataType.UWORD, true).isSplitWordArray shouldBe true
}
test("array of strings becomes array of uword pointers") {
val src="""
main {
sub start() {
str variable = "name1"
str[2] @shared names = [ variable, "name2" ]
}
}"""
val result = compileText(C64Target(), false, src, writeAssembly = true)
result shouldNotBe null
val st1 = result!!.compilerAst.entrypoint.statements
st1.size shouldBe 3
st1[0] shouldBe instanceOf<VarDecl>()
st1[1] shouldBe instanceOf<VarDecl>()
(st1[0] as VarDecl).name shouldBe "variable"
(st1[1] as VarDecl).name shouldBe "names"
val array1 = (st1[1] as VarDecl).value as ArrayLiteral
array1.type.isArray shouldBe true
array1.type.getOrUndef() shouldBe DataType.arrayFor(BaseDataType.UWORD, false)
val ast2 = result.codegenAst!!
val st2 = ast2.entrypoint()!!.children
st2.size shouldBe 3
(st2[0] as PtVariable).name shouldBe "p8v_variable"
(st2[1] as PtVariable).name shouldBe "p8v_names"
val array2 = (st2[1] as PtVariable).value as PtArray
array2.type shouldBe DataType.arrayFor(BaseDataType.UWORD, false)
}
})

View File

@ -1,15 +1,27 @@
TODO
====
add array of strings test that it becomes an array of uword pointers
replace some when { xxx.isWord... } with (when xxx.dt) { UWORD -> ...} again
Main branch: IR funcRolRor: add this: if(arr.splitWords) TODO("rol/ror on split words array")
Main branch: possible optimization for: if x < 2/ x<1 -> if x==0 or x==1 likewise for > 253/ >254
Main branch: fix IR/VM problem with containment check:
main {
sub start() {
ubyte @shared x
Extra CHECKS/Unit tests:
codegens: check array copy (elt size)
check: try to replace a multi-comparison expression (if x==1 | x==2 | x==3 ... ) by a simple containment check.
IR: check for loop over split word array
IR: check rol ror in array (elt size)
IR: check set lsb/msb in array (elt size)
x=3
if x==1 or x==2 or x==3 or x==4
txt.print("yep1\n")
x = 9
if x==1 or x==2 or x==3 or x==4
txt.print("yep2-shouldn't-see-this\n") ; TODO fix this in IR/VM
if x in 1 to 4
txt.print("yep3-shouldn't-see-this\n")
if x in 4 to 10
txt.print("yep4\n")
}
}
Improve register load order in subroutine call args assignments:

View File

@ -4,16 +4,26 @@
main {
sub start() {
str name1 = "name1"
str name2 = "name2"
uword[] @split names = [name1, name2, "name3"]
txt.print(names[2])
uword[2] array = [$1111,$eeee]
uword[2] @split sarray = [$1111,$eeee]
;uword[] @split names2 = [name1, name2, "name3"]
;uword[] addresses = [0,0,0]
;names = [1111,2222,3333]
;addresses = names
;names = addresses
;names2 = names
txt.print_uwhex(array[1], true)
txt.nl()
txt.print_uwhex(sarray[1], true)
txt.nl()
setmsb(array[1], $55)
setmsb(sarray[1], $55)
txt.print_uwhex(array[1], true)
txt.nl()
txt.print_uwhex(sarray[1], true)
txt.nl()
setlsb(array[1], $44)
setlsb(sarray[1], $44)
txt.print_uwhex(array[1], true)
txt.nl()
txt.print_uwhex(sarray[1], true)
txt.nl()
}
}

View File

@ -249,22 +249,23 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_lsb=$lsbValue zp=${variable.zpwish}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb=$msbValue zp=${variable.zpwish}\n")
} else {
val dt = variable.dt
val value: String = when {
variable.dt.isBool -> variable.onetimeInitializationNumericValue?.toInt()?.toString() ?: ""
variable.dt.isFloat -> (variable.onetimeInitializationNumericValue ?: "").toString()
variable.dt.isNumeric -> variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: ""
variable.dt.isString -> {
dt.isBool -> variable.onetimeInitializationNumericValue?.toInt()?.toString() ?: ""
dt.isFloat -> (variable.onetimeInitializationNumericValue ?: "").toString()
dt.isNumeric -> variable.onetimeInitializationNumericValue?.toInt()?.toHex() ?: ""
dt.isString -> {
val encoded = irProgram.encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue.second) + listOf(0u)
encoded.joinToString(",") { it.toInt().toString() }
}
variable.dt.isFloatArray -> {
dt.isFloatArray -> {
if(variable.onetimeInitializationArrayValue!=null) {
variable.onetimeInitializationArrayValue.joinToString(",") { it.number!!.toString() }
} else {
"" // array will be zero'd out at program start
}
}
variable.dt.isArray -> {
dt.isArray -> {
if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue.joinToString(",") {
if(it.number!=null)

View File

@ -199,23 +199,24 @@ class VmProgramLoader {
// zero out uninitialized ('bss') variables.
if(variable.uninitialized) {
if(variable.dt.isArray) {
val dt = variable.dt
repeat(variable.length!!) {
when {
variable.dt.isString || variable.dt.isBoolArray || variable.dt.isByteArray -> {
dt.isString || dt.isBoolArray || dt.isByteArray -> {
memory.setUB(addr, 0u)
addr++
}
variable.dt.isSplitWordArray -> {
dt.isSplitWordArray -> {
// lo bytes come after the hi bytes
memory.setUB(addr, 0u)
memory.setUB(addr+variable.length!!, 0u)
addr++
}
variable.dt.isWordArray -> {
dt.isWordArray -> {
memory.setUW(addr, 0u)
addr += 2
}
variable.dt.isFloatArray -> {
dt.isFloatArray -> {
memory.setFloat(addr, 0.0)
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
}