allow "not xx in array" expression in 6502 codegen

fix compiler crash on certain bool to byte casts
This commit is contained in:
Irmen de Jong 2022-12-23 13:38:34 +01:00
parent d0e6a2eb8b
commit ded9ada9bc
6 changed files with 81 additions and 27 deletions

View File

@ -873,20 +873,20 @@ internal class AssignmentAsmGen(private val program: Program,
throw AssemblyError("containment check of floats not supported")
}
DataType.ARRAY_B, DataType.ARRAY_UB -> {
val arrayVal = variable.value as ArrayLiteral
val numElements = variable.arraysize!!.constIndex()!!
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.saveRegisterLocal(CpuRegister.A, containment.definingSubroutine!!)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
asmgen.restoreRegisterLocal(CpuRegister.A)
asmgen.out(" ldy #${arrayVal.value.size}")
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray")
return
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
val arrayVal = variable.value as ArrayLiteral
val numElements = variable.arraysize!!.constIndex()!!
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #${arrayVal.value.size}")
asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_wordarray")
return
}
@ -1005,7 +1005,7 @@ internal class AssignmentAsmGen(private val program: Program,
}
}
if(valueDt==DataType.UBYTE) {
if(valueDt==DataType.UBYTE || valueDt==DataType.BOOL) {
when(target.register) {
RegisterOrPair.A,
RegisterOrPair.X,
@ -1069,8 +1069,10 @@ internal class AssignmentAsmGen(private val program: Program,
}
}
if(targetDt in IntegerDatatypes && valueDt in IntegerDatatypes && valueDt.isAssignableTo(targetDt)) {
require(targetDt in WordDatatypes && valueDt in ByteDatatypes) { "should be byte to word assignment ${origTypeCastExpression.position}"}
if(targetDt in IntegerDatatypes && valueDt in IntegerDatatypes && valueDt!=targetDt && valueDt.isAssignableTo(targetDt)) {
require(targetDt in WordDatatypes && valueDt in ByteDatatypes) {
"should be byte to word assignment ${origTypeCastExpression.position}"
}
when(target.kind) {
// TargetStorageKind.VARIABLE -> {
// This has been handled already earlier on line 961.
@ -1136,13 +1138,12 @@ internal class AssignmentAsmGen(private val program: Program,
asmgen.out(" jsr floats.MOVEF")
}
return
} else {
// No more special optmized cases yet. Do the rest via more complex evaluation
// note: cannot use assignTypeCastedValue because that is ourselves :P
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
asmgen.assignExpressionTo(origTypeCastExpression, target)
return
}
// No more special optmized cases yet. Do the rest via more complex evaluation
// note: cannot use assignTypeCastedValue because that is ourselves :P
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
asmgen.assignExpressionTo(origTypeCastExpression, target)
}
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) {

View File

@ -7,6 +7,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.compiler.BuiltinFunctions
import prog8.compiler.InplaceModifyingBuiltinFunctions
import prog8.compiler.builtinFunctionReturnType
@ -1259,8 +1260,14 @@ internal class AstChecker(private val program: Program,
val elementDt = containment.element.inferType(program)
val iterableDt = containment.iterable.inferType(program)
if(containment.parent is BinaryExpression)
errors.err("containment check is currently not supported inside complex expressions", containment.position)
if(compilerOptions.compTarget.name!=VMTarget.NAME) {
val parentBinexpr = containment.parent as? BinaryExpression
if(parentBinexpr!=null) {
// only supported if compared to 1 or 0, more complex expressions aren't supported in the 6502 code-gen.
if(parentBinexpr.operator!="==" || parentBinexpr.right.constValue(program)?.number !in listOf(0.0, 1.0))
errors.err("containment check is currently not supported inside complex expressions", containment.position)
}
}
if(iterableDt.isIterable && containment.iterable !is RangeExpression) {
val iterableEltDt = ArrayToElementTypes.getValue(iterableDt.getOr(DataType.UNDEFINED))

View File

@ -6,10 +6,7 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.Assignment
import prog8.ast.statements.ConditionalBranch
import prog8.ast.statements.IfElse
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.*
@ -175,6 +172,19 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
return noModifications
}
fun checkArray(variable: VarDecl): Iterable<IAstModification> {
return if(variable.value==null) {
val arraySpec = variable.arraysize!!
val size = arraySpec.indexExpr.constValue(program)?.number?.toInt() ?: throw FatalAstException("no array size")
return if(size==0)
replaceWithFalse()
else
noModifications
}
else
checkArray((variable.value as ArrayLiteral).value)
}
fun checkString(stringVal: StringLiteral): Iterable<IAstModification> {
if(stringVal.value.isEmpty())
return replaceWithFalse()
@ -198,8 +208,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
return checkString(stringVal)
}
in ArrayDatatypes -> {
val array = (variable.value as ArrayLiteral).value
return checkArray(array)
return checkArray(variable)
}
else -> {}
}

View File

@ -0,0 +1,22 @@
package prog8tests.codegeneration
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldNotBe
import prog8.code.target.C64Target
import prog8tests.helpers.compileText
class TestVarious: FunSpec({
test("bool to byte cast in expression is correct") {
val text="""
main {
sub start() {
ubyte[3] values
func(33 + (22 in values))
}
sub func(ubyte arg) {
arg++
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
})

View File

@ -327,4 +327,19 @@ main {
instructions.size shouldBe 18
instructions.last().opcode shouldBe Opcode.RETURN
}
test("compile virtual: various expressions") {
val text="""
main {
sub start() {
ubyte[3] values = [1,2,3]
func(33 + (22 in values)) ; bool cast to byte
func(values[cx16.r0L] + (22 in values)) ; containment in complex expression
}
sub func(ubyte arg) {
arg++
}
}"""
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
})

View File

@ -3,12 +3,12 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- bool bb = not bb -> larger code than bool bb ^= 1
- bool xx = ~xx -> larger code than xx ^= 1
- ubyte xx = not xx -> much larger code than xx ^= 1
- try to support "not (xx in array)" or maybe even "xx not in array"
- astchecker: "containment check is currently not supported inside complex expressions" fix this?
or at least disable this check for virtual target?
- allow "xx not in array" and rewrite it into "not xx in array"
- make sure bool value is always 0 or 1 (all casts should convert), then:
- rewrite bool=bool^1 into bool=not bool
- should solve: bool bb = not bb -> larger code than bool bb ^= 1
- should solve: bool xx = ~xx -> larger code than xx ^= 1
...