support a few other simple cases of struct instance assignment

This commit is contained in:
Irmen de Jong
2025-08-21 00:04:30 +02:00
parent f93d957999
commit fad17cc094
5 changed files with 92 additions and 36 deletions
@@ -742,6 +742,8 @@ _after:
if(targetDt.isStructInstance && sourceDt.isStructInstance) {
if(targetDt == sourceDt) {
// special case simple struct instance assignment via memory copy
val size = program.memsizer.memorySize(sourceDt.getOrUndef(), null)
val structSizeNum = NumericLiteral.optimalInteger(size, assignment.position)
val deref = assignment.value as? PtrDereference
if(deref!=null) {
val sourcePtr = IdentifierReference(deref.chain, assignment.position)
@@ -749,15 +751,50 @@ _after:
if(targetDeref!=null) {
// ptr1^^ = ptr2^^ --> memcopy(ptr2, ptr1, sizeof(struct))
val targetPtr = IdentifierReference(targetDeref.chain, assignment.position)
val size = program.memsizer.memorySize(sourceDt.getOrUndef(), null)
require(program.memsizer.memorySize(targetDt.getOrUndef(), null)==size)
val numBytes = NumericLiteral.optimalInteger(size, assignment.position)
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assignment.position),
mutableListOf(sourcePtr, targetPtr, numBytes),
mutableListOf(sourcePtr, targetPtr, structSizeNum),
false, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, memcopy, parent))
}
val targetIdx = assignment.target.arrayindexed
if(targetIdx!=null) {
val constIndex = targetIdx.indexer.constIndex()
if(constIndex!=null) {
val idxType = targetIdx.inferType(program)
if (idxType.isStructInstance) {
// ptr1[idx]^^ = ptr2^^ --> memcopy(ptr2, ptr1+idx*sizeof(struct), sizseof(struct))
val offset = NumericLiteral.optimalInteger(constIndex * size, assignment.position)
val target = BinaryExpression(targetIdx.plainarrayvar!!, "+", offset, assignment.position)
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assignment.position),
mutableListOf(sourcePtr, target, structSizeNum),
false, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, memcopy, parent))
}
}
}
}
val sourceIdx = assignment.value as? ArrayIndexedExpression
if(sourceIdx!=null) {
val constIndex = sourceIdx.indexer.constIndex()
if(constIndex!=null) {
val idxType = sourceIdx.inferType(program)
if(idxType.isStructInstance) {
val targetDeref = assignment.target.pointerDereference
if(targetDeref!=null) {
// ptr1^^ = ptr2[idx] --> memcopy(ptr2 + idx*sizeof(struct), ptr1, sizeof(struct))
val offset = NumericLiteral.optimalInteger(constIndex * size, assignment.position)
val targetPtr = IdentifierReference(targetDeref.chain, assignment.position)
val source = BinaryExpression(sourceIdx.plainarrayvar!!, "+", offset, assignment.position)
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assignment.position),
mutableListOf(source, targetPtr, structSizeNum),
false, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, memcopy, parent))
}
}
}
}
// TODO support other forms of struct instance assignments, such as ptr^^ = array[2]^^ , array[3] = array[2], array[3] = ptr^^
}
}
+30 -5
View File
@@ -1203,6 +1203,33 @@ main {
errors.errors[3] shouldContain "assigning this value to struct instance not supported"
}
xtest("assigning struct instances") {
val src="""
main {
sub start() {
struct List {
bool b
uword value
float fv
} ; sizeof = 11
^^List lp1 = 10000
^^List lp2 = 20000
lp2^^ = lp1^^ ; memcopy(lp1, lp2, 11)
lp2[2] = lp1^^ ; memcopy(lp1, lp2 + 22, 11)
lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above) TODO fix astchecker to allow this case
lp2^^ = lp1[2] ; memcopy(lp1 + 22, lp2, 11)
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above) TODO fix astchecker to allow this case
lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(VMTarget(), false, src, outputDir, errors=errors)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 99
}
test("a.b.c[i]^^.value as expression where pointer is struct") {
val src="""
main {
@@ -1870,17 +1897,15 @@ main {
^^Node nodes
n1^^=n2^^ ; ok
bptr1^^=bptr2^^ ; ok
n2 = nodes[2]^^ ; cannot assign instance to pointer
n1^^=nodes[2] ; cannot assign struct instances like this
n1^^=nodes[2] ; ok
n2 = nodes[2]^^ ; cannot assign instance to pointer like this yet
}
}"""
val errors=ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "struct instance by value"
errors.errors[1] shouldContain "no support for getting the target value of pointer array indexing"
errors.errors[2] shouldContain "struct instance by value"
errors.errors[3] shouldContain "assigning this value to struct instance not supported"
}
test("pointer variable usage detection in other block 1") {
+5 -2
View File
@@ -151,11 +151,14 @@ The struct type creates a new name scape, so accessing the fields of a struct is
.. note::
Structs are only supported as a *reference type* (via a pointer). It is currently not possible to use them as a value type.
This means you cannot create an array of structs either - only arrays of pointers to structs.
There is one simple case where the compiler allows assignment of struct instances. You are allowed to write::
There are a couple of simple case where the compiler allows assignment of struct instances. You are allowed to write::
ptr1^^ = ptr2^^ ; set what ptr1 points to, to the contents of what ptr2 points to
ptr2^^ = ptr1^^
ptr2^^ = ptr1[2]
ptr2[2] = ptr1^^
The compiler replaces this with a memory copy if these are pointers to a struct.
In the future more cases may be supported.
.. note::
Using structs instead of plain arrays may result in less efficent code being generated.
+1 -1
View File
@@ -5,7 +5,6 @@ STRUCTS and TYPED POINTERS (6502 codegen specific)
--------------------------------------------------
- implement the TODO's in PointerAssignmentsGen.
- implement some more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO)
- scan through 6502 library modules to change untyped uword pointers to typed pointers
- scan through 6502 examples to change untyped uword pointers to typed pointers
- fix code size regressions (if any left)
@@ -14,3 +13,4 @@ STRUCTS and TYPED POINTERS (6502 codegen specific)
- optimize deref() when field offset is 0, don't use a temporary at all if the var is in zp already (maybe already solved by the previous one?)
- optimize the multiplications in assignAddressOfIndexedPointer()
- optimize the float copying in assignIndexedPointer() (also word?)
- implement some more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs')
+15 -24
View File
@@ -6,36 +6,27 @@
main {
struct List {
^^uword s
ubyte n
^^List next
}
sub start() {
ubyte[10] array
uword @shared wordptr
^^bool @shared boolptr
^^float @shared floatptr
^^byte @shared byteptr
^^ubyte @shared ubyteptr
^^List @shared listptr
^^List @shared listptr2
struct List {
bool b
uword value
float fv
} ; sizeof = 11
bool @shared zz
float @shared fl
byte @shared bb
^^List lp1 = 10000
^^List lp2 = 20000
zz = boolptr[999]
fl = floatptr[999]
bb = byteptr[999]
cx16.r0L = ubyteptr[999]
cx16.r1L = wordptr[999]
cx16.r2L = array[9]
lp2^^ = lp1^^ ; memcopy(lp1, lp2, 11)
lp2[2] = lp1^^ ; memcopy(lp1, lp2 + 22, 11)
; lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above) TODO fix astchecker to allow this case
lp2^^ = lp1[2] ; memcopy(lp1 + 22, lp2, 11)
; lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above) TODO fix astchecker to allow this case
; lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
}
}
;main {
; sub start() {
; floatprob()