6502 statementreorderer: str -> ^^ubyte

This commit is contained in:
Irmen de Jong
2025-08-13 18:45:29 +02:00
parent 8880ed1393
commit fd9bd23449
19 changed files with 183 additions and 182 deletions

View File

@@ -542,9 +542,6 @@
<inspection_tool class="HardcodedLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HardwiredNamespacePrefix" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HashCodeUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HelmChartMissingKeys" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="HelmChartUnknownKeys" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HelmChartUnknownValues" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="HibernateConfigDomFacetInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HibernateConfigDomInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="HibernateFindAnnotationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
@@ -802,15 +799,6 @@
<inspection_tool class="KeySetIterationMayUseEntrySet" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="KotlinBigDecimalEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="KtorYamlConfig" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="KubernetesDeprecatedKeys" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="KubernetesDeprecatedResources" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="KubernetesDuplicatedEnvVars" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="KubernetesMissingKeys" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="KubernetesNonEditableKeys" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="KubernetesNonEditableResources" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="KubernetesUnknownKeys" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="KubernetesUnknownResourcesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="KubernetesUnknownValues" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="LabeledStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LambdaParameterHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
@@ -1161,7 +1149,6 @@
</inspection_tool>
<inspection_tool class="ReturnOfInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReturnThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RunBlockingInSuspendFunction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RuntimeExec" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RuntimeExecWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SafeLock" enabled="true" level="WARNING" enabled_by_default="true" />

View File

@@ -747,6 +747,7 @@ class AsmGen6502Internal (
assignExpressionToRegister(value, RegisterOrPair.A)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false)
}
target.datatype.isPointer -> TODO("assign expression to pointer ${target.position}")
target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY)
translateNormalAssignment(
@@ -1087,7 +1088,8 @@ $repeatLabel""")
val returnRegs = sub.returnsWhatWhere()
if(returnvalue!=null) {
if (sub.signature.returns.single().isNumericOrBool) {
val returnDt = sub.signature.returns.single()
if (returnDt.isNumericOrBool || returnDt.isPointer) {
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
}
else {

View File

@@ -45,7 +45,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
val rightDt = compareCond.right.type
return when {
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
rightDt.isWord -> translateIfWord(stmt, compareCond, jumpAfterIf)
rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
else -> throw AssemblyError("weird dt")
}

View File

@@ -446,32 +446,22 @@ internal class AssignmentAsmGen(
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
when(val value = assign.source.expression!!) {
is PtAddressOf -> {
val identifier = value.identifier
if (identifier != null) {
if (value.identifier != null || value.isFromArrayElement) {
val identifier = value.identifier!!
val source = asmgen.symbolTable.lookup(identifier.name)
require(source !is StConstant) { "addressOf of a constant should have been rewritten to a simple addition expression" }
val arrayDt = identifier.type
val sourceName =
if (value.isMsbForSplitArray)
asmgen.asmSymbolName(identifier) + "_msb"
else if (arrayDt.isSplitWordArray)
else if (identifier.type.isSplitWordArray)
asmgen.asmSymbolName(identifier) + "_lsb" // the _lsb split array comes first in memory
else
asmgen.asmSymbolName(identifier)
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, arrayDt, value.arrayIndexExpr)
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, identifier.type, value.arrayIndexExpr)
} else {
val ptrderef = value.dereference
if(ptrderef!=null) {
val zpPtrVar = pointergen.deref(ptrderef)
assignVariableWord(assign.target, zpPtrVar, DataType.UWORD)
} else {
val array = value.arrayIndexExpr
if(array!=null) {
TODO("assign &array indexed ${assign.position}")
} else {
throw AssemblyError("weird addressOf value ${value.position}")
}
}
val ptrderef = value.dereference!!
val zpPtrVar = pointergen.deref(ptrderef)
assignVariableWord(assign.target, zpPtrVar, DataType.UWORD)
}
}
is PtBool -> throw AssemblyError("source kind should have been literalboolean")
@@ -2161,7 +2151,16 @@ $endLabel""")
return
}
}
is PtNumber, is PtBool -> throw AssemblyError("a cast of a literal value should have been const-folded away")
is PtNumber -> {
if(targetDt.isPointer) {
// assign a number to a pointer type
require(valueDt.isInteger)
assignConstantWord(target, value.number.toInt())
}
else
throw AssemblyError("literal value cast should have been const-folded away (target type=$targetDt)")
}
is PtBool -> throw AssemblyError("literal value cast should have been const-folded away (target type=$targetDt)")
is PtArrayIndexer -> {
if(targetDt.isByte && valueDt.isWord) {
// just assign the lsb from the array value

View File

@@ -123,6 +123,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
}
target.datatype.isPointer ->
ptrgen.inplaceModification(PtrTarget(target), operator, value)
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
}
}
@@ -307,7 +309,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
}
target.datatype.isPointer -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
}
}
@@ -510,7 +512,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
pla ; restore array ptr lsb
jsr floats.copy_float""")
}
target.datatype.isPointer -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
}
}

View File

@@ -615,7 +615,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=actualResultReg2, immediate = 1)
}
}
else -> throw AssemblyError("weird cast value type")
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
BaseDataType.UBYTE -> {
@@ -631,7 +631,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
BaseDataType.BYTE -> {
@@ -647,7 +647,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
BaseDataType.UWORD -> {
@@ -672,7 +672,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.POINTER -> {
actualResultReg2 = tr.resultReg
}
else -> throw AssemblyError("weird cast value type")
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
BaseDataType.WORD -> {
@@ -694,7 +694,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
BaseDataType.FLOAT -> {
@@ -712,7 +712,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.WORD -> {
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
else -> throw AssemblyError("weird cast value type")
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
BaseDataType.POINTER -> {
@@ -723,7 +723,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.ARRAY_POINTER -> {
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
}
else -> throw AssemblyError("weird cast type")
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2)

View File

@@ -242,14 +242,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
if (rightVal?.number == 1.0) {
if (rightDt != leftDt) {
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
else if (rightVal?.number == 0.0) {
if (rightDt != leftDt) {
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
@@ -262,14 +264,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
if (rightVal?.number == 1.0) {
if(rightDt!=leftDt) {
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
else if (rightVal?.number == 0.0) {
if(rightDt!=leftDt) {
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}

View File

@@ -108,7 +108,7 @@ strings {
repeat cx16.r2L {
if startswith(cx16.r3, needle) {
sys.set_carry()
return cx16.r3-haystack as ubyte
return cx16.r3-(haystack as uword) as ubyte
}
cx16.r3++
}

View File

@@ -181,8 +181,8 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
val symbolTable = stMaker.make()
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
args.errors.report()
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
args.errors.report()
if (compilationOptions.optimize) {
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)

View File

@@ -696,8 +696,10 @@ internal class AstChecker(private val program: Program,
// unfortunately the AST regarding pointer dereferencing is a bit of a mess, and we cannot do precise type checking on elements inside such expressions yet.
if(assignment.value.inferType(program).isUnknown) {
val binexpr = assignment.value as? BinaryExpression
if (binexpr?.operator != ".") {
if(assignment.value !is PtrDereference && assignment.target.multi==null)
if(assignment.target.multi==null) {
if (binexpr != null && binexpr.operator != ".")
errors.err("invalid assignment value", assignment.value.position)
else
errors.err("invalid assignment value", assignment.value.position)
}
}
@@ -833,7 +835,8 @@ internal class AstChecker(private val program: Program,
if(trueDt.isUnknown || falseDt.isUnknown) {
errors.err("invalid value type(s)", ifExpr.position)
} else if(trueDt!=falseDt) {
errors.err("both values should be the same type", ifExpr.truevalue.position)
// if (!(trueDt.isUnsignedWord && falseDt.isPointer || trueDt.isPointer && falseDt.isUnsignedWord))
errors.err("both values in the if expression should be the same type", ifExpr.truevalue.position)
}
super.visit(ifExpr)
}
@@ -1510,6 +1513,10 @@ internal class AstChecker(private val program: Program,
}
}
}
"-" -> {
if(leftDt.isNumeric && rightDt.isPointer)
errors.err("unclear pointer arithmetic in expression, perhaps you meant to just subtract addresses? Cast pointer to uword in that case.", expr.right.position)
}
}
if(!leftDt.isNumeric && !leftDt.isString && !leftDt.isBool && !leftDt.isPointer)
@@ -2472,6 +2479,9 @@ internal class AstChecker(private val program: Program,
}
else if(targetDatatype.isString && sourceDatatype.isUnsignedWord)
errors.err("can't assign uword to str. If the source is a string pointer and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
else if(targetDatatype.isString && sourceDatatype == DataType.pointer(BaseDataType.UBYTE)) {
// this is allowed: assigning ^^ubyte to a str (will use stringcopy)
}
else if(targetDatatype.isStructInstance) {
if(sourceDatatype.isStructInstance && sourceDatatype != targetDatatype)
errors.err("value type $sourceDatatype doesn't match target type $targetDatatype", position)

View File

@@ -220,9 +220,9 @@ internal class StatementReorderer(
subs.map { IAstModification.InsertLast(it, subroutine) }
}
// change 'str' and 'ubyte[]' parameters or return types into ^^ubyte (TODO also for 6502 target, that is still uword for now)
// change 'str' and 'ubyte[]' parameters or return types into ^^ubyte
val stringParams = subroutine.parameters.filter { it.type.isString || it.type.isUnsignedByteArray }
val replacementForStrDt = if(options.compTarget.cpu!=CpuType.VIRTUAL) DataType.UWORD else DataType.pointer(BaseDataType.UBYTE) // TODO fix this once 6502 has pointers too
val replacementForStrDt = DataType.pointer(BaseDataType.UBYTE)
val parameterChanges = stringParams.map {
val uwordParam = SubroutineParameter(it.name, replacementForStrDt, it.zp, it.registerOrPair, it.position)
IAstModification.ReplaceNode(it, uwordParam, subroutine)
@@ -240,9 +240,9 @@ internal class StatementReorderer(
subroutine.statements
.asSequence()
.filterIsInstance<VarDecl>()
.filter { it.origin==VarDeclOrigin.SUBROUTINEPARAM && it.name in stringParamsByNames }
.filter { it.origin==VarDeclOrigin.SUBROUTINEPARAM && it.name in stringParamsByNames && it.datatype!=DataType.pointer(BaseDataType.UBYTE) }
.map {
val newvar = VarDecl(it.type, it.origin, DataType.UWORD,
val newvar = VarDecl(it.type, it.origin, DataType.pointer(BaseDataType.UBYTE),
it.zeropage,
it.splitwordarray,
null,
@@ -299,9 +299,11 @@ internal class StatementReorderer(
}
if(!assignment.isAugmentable) {
if (valueType issimpletype BaseDataType.STR && targetType issimpletype BaseDataType.STR) {
// replace string assignment by a call to stringcopy
return copyStringValue(assignment)
if (targetType issimpletype BaseDataType.STR) {
if (valueType issimpletype BaseDataType.STR || valueType.getOrUndef() == DataType.pointer(BaseDataType.UBYTE)) {
// replace string assignment by a call to stringcopy
return copyStringValue(assignment)
}
}
}

View File

@@ -180,6 +180,12 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
}
if(typecast.type.isWord && typecast.expression.inferType(program).isPointer) {
// pointers can be assigned to untyped pointer (uword) without a cast, but not if it's part of an expression!
if(parent !is Expression)
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
}
return noModifications
}

View File

@@ -776,6 +776,32 @@ main {
(a4v.right as TypecastExpression).expression shouldBe instanceOf<IdentifierReference>()
}
test("odd pointer arithmetic") {
val src="""
main{
sub start() {
^^ubyte @shared ptr = 2000
cx16.r0L = (cx16.r1 - ptr) as ubyte
cx16.r1L = (cx16.r1 - (ptr as uword)) as ubyte
void findstr1("asdf")
void findstr2("asdf")
}
sub findstr1(str haystack) -> ubyte {
return (cx16.r3-haystack) as ubyte
}
sub findstr2(str haystack) -> ubyte {
return (cx16.r3-(haystack as uword)) as ubyte
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = false) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain("6:31: unclear pointer arithmetic in expression")
errors.errors[1] shouldContain("13:25: unclear pointer arithmetic in expression")
}
test("uword struct field array indexing") {
val src="""
main {
@@ -1355,6 +1381,29 @@ main {
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
}
test("str replaced by ^^ubyte in subroutine args and return type") {
val src="""
main {
sub start() {
void test("hello")
}
sub test(str argument) -> str {
return "bye"
}
}"""
val vmprg = compileText(VMTarget(), false, src, outputDir)!!
val vmtest = vmprg.codegenAst!!.allBlocks().first { it.name == "main" }.children[1] as PtSub
vmtest.signature.returns.single() shouldBe DataType.pointer(BaseDataType.UBYTE)
(vmtest.signature.children.single() as PtSubroutineParameter).type shouldBe DataType.pointer(BaseDataType.UBYTE)
val c64prg = compileText(C64Target(), false, src, outputDir)!!
val c64test = c64prg.codegenAst!!.allBlocks().first { it.name == "p8b_main" }.children[1] as PtSub
c64test.signature.returns.single() shouldBe DataType.pointer(BaseDataType.UBYTE)
(c64test.signature.children.single() as PtSubroutineParameter).type shouldBe DataType.pointer(BaseDataType.UBYTE)
}
test("hoist variable decl and initializer correctly in case of pointer type variable as well") {
val src="""
%import textio

View File

@@ -10,6 +10,7 @@ import io.kotest.matchers.types.instanceOf
import prog8.ast.IFunctionCall
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.*
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.target.C64Target
import prog8.code.target.VMTarget
@@ -41,30 +42,29 @@ class TestSubroutines: FunSpec({
test("stringParameter") {
val text = """
main {
sub start() {
str text = "test"
asmfunc("text")
asmfunc(text)
asmfunc($2000)
func("text")
func(text)
func($2000)
}
asmsub asmfunc(str thing @AY) {
%asm {{
rts
}}
}
main {
sub start() {
str text = "test"
asmfunc("text")
asmfunc(text)
asmfunc($2000)
func("text")
func(text)
func($2000)
}
asmsub asmfunc(str thing @AY) {
%asm {{
rts
}}
}
sub func(str thing) {
uword t2 = thing as uword
asmfunc(thing)
}
}
"""
sub func(str thing) {
uword t2 = thing as uword
asmfunc(thing)
}
}"""
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
val mainBlock = result.compilerAst.entrypoint.definingBlock
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
@@ -72,13 +72,14 @@ class TestSubroutines: FunSpec({
asmfunc.isAsmSubroutine shouldBe true
asmfunc.statements.size shouldBe 1
func.isAsmSubroutine shouldBe false
withClue("str param for subroutines should be changed into UWORD") {
asmfunc.parameters.single().type shouldBe DataType.UWORD
func.parameters.single().type shouldBe DataType.UWORD
withClue("str param for subroutines should be changed into ^^ubyte") {
asmfunc.parameters.single().type shouldBe DataType.pointer(BaseDataType.UBYTE)
func.parameters.single().type shouldBe DataType.pointer(BaseDataType.UBYTE)
func.statements.size shouldBe 5
val paramvar = func.statements[0] as VarDecl
paramvar.name shouldBe "thing"
paramvar.datatype shouldBe DataType.UWORD
paramvar.origin shouldBe VarDeclOrigin.SUBROUTINEPARAM
paramvar.datatype shouldBe DataType.pointer(BaseDataType.UBYTE)
}
val assign = func.statements[2] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("t2")
@@ -133,17 +134,17 @@ class TestSubroutines: FunSpec({
(asmfunc.statements.single() as InlineAssembly).assembly.trim() shouldBe "rts"
asmfunc.hasRtsInAsm(false) shouldBe true
func.isAsmSubroutine shouldBe false
withClue("str param should have been changed to uword") {
asmfunc.parameters.single().type shouldBe DataType.UWORD
func.parameters.single().type shouldBe DataType.UWORD
withClue("str param should have been changed to ^^ubyte") {
asmfunc.parameters.single().type shouldBe DataType.pointer(BaseDataType.UBYTE)
func.parameters.single().type shouldBe DataType.pointer(BaseDataType.UBYTE)
}
func.statements.size shouldBe 5
func.statements[4] shouldBe instanceOf<Return>()
val paramvar = func.statements[0] as VarDecl
paramvar.name shouldBe "thing"
withClue("pre-asmgen should have changed str to uword type") {
paramvar.datatype shouldBe DataType.UWORD
withClue("pre-asmgen should have changed str to ^^ubyte type") {
paramvar.datatype shouldBe DataType.pointer(BaseDataType.UBYTE)
}
val assign = func.statements[2] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("t2")
@@ -191,9 +192,9 @@ class TestSubroutines: FunSpec({
val mainBlock = result.compilerAst.entrypoint.definingBlock
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
withClue("ubyte array param should have been replaced by UWORD pointer") {
asmfunc.parameters.single().type shouldBe DataType.UWORD
func.parameters.single().type shouldBe DataType.UWORD
withClue("ubyte array param should have been replaced by ^^ubyte pointer") {
asmfunc.parameters.single().type shouldBe DataType.pointer(BaseDataType.UBYTE)
func.parameters.single().type shouldBe DataType.pointer(BaseDataType.UBYTE)
}
}

View File

@@ -12,9 +12,6 @@ import prog8.ast.expressions.*
import prog8.ast.printProgram
import prog8.ast.statements.Assignment
import prog8.ast.statements.IfElse
import prog8.code.ast.PtAsmSub
import prog8.code.ast.PtSub
import prog8.code.ast.PtSubroutineParameter
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.Position
@@ -759,37 +756,6 @@ main {
errors.errors[3] shouldContain (":9:28: value type uword doesn't match target")
}
test("str replaced with uword in subroutine params and return types (6502 only until that has pointers too)") { // TODO remove this test once 6502 has pointers too
val src = """
main {
sub start() {
derp("hello")
mult3("hello")
}
sub derp(str arg) -> str {
cx16.r0++
return arg
}
asmsub mult3(str input @XY) -> str @XY {
%asm {{
ldx #100
ldy #101
rts
}}
}
}"""
val result = compileText(C64Target(), true, src, outputDir, writeAssembly = true)!!
val main = result.codegenAst!!.allBlocks().first()
val derp = main.children.single { it is PtSub && it.name == "p8s_derp" } as PtSub
derp.signature.returns shouldBe listOf(DataType.UWORD)
(derp.signature.children.single() as PtSubroutineParameter).type shouldBe DataType.UWORD
val mult3 = main.children.single { it is PtAsmSub && it.name == "p8s_mult3" } as PtAsmSub
mult3.parameters.single().second.type shouldBe DataType.UWORD
mult3.returns.single().second shouldBe DataType.UWORD
}
test("return 0 for str converted to uword") {
val src = """
main {
@@ -805,6 +771,7 @@ main {
return 42
}
}"""
compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
}

View File

@@ -189,6 +189,10 @@ class BinaryExpression(
"+", "-", "*", "%", "/" -> {
if (!leftDt.isKnown || !rightDt.isKnown)
InferredTypes.unknown()
else if(operator=="-" && leftDt.isNumeric && rightDt.isPointer) {
// x - pointer is not pointer arithmetic like pointer-x or pointer+x, it's invalid, but return untyped pointer uword here
return InferredTypes.knownFor(BaseDataType.UWORD)
}
else {
try {
val dt = InferredTypes.knownFor(
@@ -478,7 +482,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
override fun referencesIdentifier(nameInSource: List<String>) = expression.referencesIdentifier(nameInSource)
override fun inferType(program: Program) = InferredTypes.knownFor(type)
override fun constValue(program: Program): NumericLiteral? {
if(!type.isBasic)
if(!type.isBasic && !type.isPointer)
return null
val cv = expression.constValue(program) ?: return null
cv.linkParents(parent)
@@ -628,7 +632,9 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
BaseDataType.WORD -> require(number in -32768.0..32767.0)
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
BaseDataType.BOOL -> require(number==0.0 || number==1.0)
else -> require(type.isNumericOrBool) { "numeric literal type should be numeric or bool: $type" }
BaseDataType.POINTER -> throw FatalAstException("pointer literals should not be created, should have been UWORD $position")
else -> require(type.isNumericOrBool) {
"numeric literal type should be numeric or bool: $type" }
}
if(type!=BaseDataType.FLOAT) {
if(truncate(number) != number)
@@ -829,6 +835,7 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
if(targettype==BaseDataType.LONG)
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
// note: do not reduce a number casted to a pointer, back to a number...
}
BaseDataType.WORD -> {
if(targettype==BaseDataType.BYTE && number >= -128 && number <=127)

View File

@@ -272,7 +272,11 @@ class VarDecl(
decltype = VarDeclType.MEMORY
value = AddressOf(IdentifierReference(regname, param.position), null, null, false, false,param.position)
}
val dt = if(param.type.isArray) DataType.UWORD else param.type
val dt = when {
param.type.isArray -> DataType.UWORD
param.type.isString -> DataType.pointer(BaseDataType.UBYTE)
else -> param.type
}
return VarDecl(decltype, VarDeclOrigin.SUBROUTINEPARAM, dt, param.zp, SplitWish.DONTCARE, null, param.name, emptyList(), value,
sharedWithAsm = false,
alignment = 0u,

View File

@@ -5,7 +5,6 @@ TODO
STRUCTS and TYPED POINTERS (6502 codegen specific)
--------------------------------------------------
- 6502 statementreorderer: fix todo for str -> ^^ubyte instead of uword
- fix struct allocations/inits.
- prefixSymbols(): what to do with prefixing struct fields? Should they be prefixed with something or no?

View File

@@ -1,52 +1,14 @@
%import floats
%import textio
%option no_sysinit
%zeropage basicsafe
main {
struct List {
uword s
ubyte n
float f
bool b
^^List next
}
sub start() {
^^List @shared l0 = 30000
^^List @shared l1 = 20000
l1.next = l0
cx16.r0 = &l1.s
cx16.r1 = &l1.n
cx16.r2 = &l1.f
cx16.r3 = &l1.b
cx16.r4 = &l1.next
cx16.r5 = &l1.next.s
cx16.r6 = &l1.next.n
cx16.r7 = &l1.next.f
cx16.r8 = &l1.next.b
cx16.r9 = &l1.next.next
label:
uword @shared addr
addr = label
addr = thing
addr = &label
addr = &thing
}
txt.print_uw(cx16.r0)
txt.spc()
txt.print_uw(cx16.r1)
txt.spc()
txt.print_uw(cx16.r2)
txt.spc()
txt.print_uw(cx16.r3)
txt.spc()
txt.print_uw(cx16.r4)
txt.nl()
txt.print_uw(cx16.r5)
txt.spc()
txt.print_uw(cx16.r6)
txt.spc()
txt.print_uw(cx16.r7)
txt.spc()
txt.print_uw(cx16.r8)
txt.spc()
txt.print_uw(cx16.r9)
txt.nl()
sub thing() {
}
}