preparing for statically allocating struct instances

This commit is contained in:
Irmen de Jong
2025-05-03 18:46:21 +02:00
parent 1ba5587404
commit 4dc82f2c83
11 changed files with 44 additions and 17 deletions

View File

@@ -629,6 +629,10 @@ internal class AstChecker(private val program: Program,
}
}
if(fcall?.target?.targetStructDecl()!=null)
// Struct(...) initializer
return super.visit(assignment)
// 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
@@ -2213,7 +2217,7 @@ internal class AstChecker(private val program: Program,
if(sourceDatatype.isPointer) {
if(!(sourceDatatype isAssignableTo targetDatatype))
errors.err("cannot assign different pointer type", position)
} else if(!sourceDatatype.isUnsignedWord)
} else if(!sourceDatatype.isUnsignedWord && !sourceDatatype.isStructInstance)
errors.err("can only assign uword or correct pointer type to a pointer", position)
}
else if(targetDatatype.isString && sourceDatatype.isUnsignedWord)

View File

@@ -227,7 +227,7 @@ internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, st
else
errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
}
is Alias -> {
is Alias, is StructDecl -> {
return targetStatement
}
null -> {

View File

@@ -252,7 +252,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
if(target.type!=VarDeclType.VAR || !target.datatype.isUnsignedWord)
errors.err("wrong address variable datatype, expected uword", call.target.position)
}
is Alias -> {}
is Alias, is StructDecl -> {}
null -> {}
else -> errors.err("cannot call this as a subroutine or function", call.target.position)
}

View File

@@ -366,7 +366,17 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
val (target, _) = srcCall.target.targetNameAndType(program)
val iType = srcCall.inferType(program)
val call = PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position)
val call =
if(iType.isStructInstance) {
// a call to a struct yields a struct instance and means: allocate a statically initialized struct instance of that type
val struct = iType.getOrUndef().subType!!
val pointertype = DataType.pointerToType(struct)
PtBuiltinFunctionCall("staticalloc", false, true, pointertype, srcCall.position)
} else {
// regular function call
PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position)
}
for (arg in srcCall.args)
call.add(transformExpression(arg))
return call

View File

@@ -9,11 +9,11 @@ import prog8.code.core.*
internal fun postprocessSimplifiedAst(program: PtProgram, st: SymbolTable, errors: IErrorReporter) {
processDefers(program, st, errors)
processSubtypes(program, st)
processSubtypesIntoStReferences(program, st)
}
private fun processSubtypes(program: PtProgram, st: SymbolTable) {
private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable) {
fun getStStruct(subType: ISubType): StStruct {
val stNode = st.lookup(subType.scopedNameString) as? StStruct

View File

@@ -1433,6 +1433,9 @@ class FunctionCallExpression(override var target: IdentifierReference,
return InferredTypes.unknown() // has multiple return types... so not a single resulting datatype possible
}
is StructDecl -> {
return InferredTypes.knownFor(DataType.structInstance(stmt))
}
else -> return InferredTypes.unknown()
}
}

View File

@@ -28,14 +28,16 @@ STRUCTS and TYPED POINTERS
- DONE (for basic types only): allow array syntax on pointers too: ptr[2] means ptr+sizeof()*2, ptr[0] just means ptr^^ .
- DONE (?) allow array syntax on pointers to structs too, but what type will ptr[2] have? And it will require ptr[2].field to work as well now. Actually that will be the only thing to work for now.
- DONE: allow multi-field declarations in structs
- is the ARRAY_POINTER data type enum realy needed? can just use ARRAY?
- are the ARRAY_POINTER and ARRAY_STRUCT data type enums realy needed? can just use ARRAY?
- pointer types in subroutine signatures (both normal and asm-subs)
- support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
- pointer arithmetic should follow C: ptr=ptr+10 adds 10*sizeof() instead of just 10.
- fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
- add unit tests for all changes
- arrays of structs? No -> Just an array of uword pointers to said structs. Can even be @split as the only representation form because that's the default for word arrays.
- static initialization of structs may be allowed only at block scope and then behaves like arrays; it won't reset to the original value when program is restarted, so beware. Syntax = TBD
- static initialization of structs may be allowed only at block scope and then behaves like arrays; it won't reset to the original value when program is restarted, so beware.
Syntax could be: ^^Node ptr = Node(1,2,3,4) statically allocates a Node with fields set to 1,2,3,4 and puts the address in ptr.
- Verify the argments to such a static struct initializer 'call' against the fields, and the same check as for static array values (const value or address-of) ?
- allow memory-mapped structs? Something like &Sprite sprite0 = $9000 basically behaves identically to a typed pointer, but the address is immutable as usual
- existing STR and ARRAY remain unchanged (don't become typed pointers) so we can keep doing register-indexed addressing directly on them
- rather than str or uword parameter types for routines with a string argument, use ^^str (or ^^ubyte maybe? these are more or less identical..?)

View File

@@ -16,10 +16,10 @@ main {
^^Node next
}
Node @shared node1,node2,node3
node1.value = 42
node1.value2 = 55
node1.value3 = 66
; proposed static initializer syntax:
; ^^Node @shared node3 = Node( 33,2,3, 0 )
; ^^Node @shared node2 = Node( 22,2,3, &node3 )
; ^^Node @shared node1 = Node( 11,2,3, &node2 )
^^Node n0,n1,n2,n3,n4,n5,n6,n7,n8

View File

@@ -253,15 +253,15 @@ class StMemVar(name: String,
class StStruct(
name: String,
val members: List<Pair<DataType, String>>,
val fields: List<Pair<DataType, String>>,
astNode: PtStructDecl?
) : StNode(name, StNodeType.STRUCT, astNode), ISubType {
fun memsize(sizer: IMemSizer): Int = members.sumOf { sizer.memorySize(it.first, 1) }
fun memsize(sizer: IMemSizer): Int = fields.sumOf { sizer.memorySize(it.first, 1) }
fun getField(name: String, sizer: IMemSizer): Pair<DataType, Int> {
// returns type and byte offset of the given field
var offset = 0
for((dt, definedname) in members) {
for((dt, definedname) in fields) {
if(name==definedname)
return dt to offset
offset += sizer.memorySize(dt, null)

View File

@@ -120,6 +120,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
// don't add memory slabs in nested scope, just put them in the top level of the ST
scope.first().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
}
else if(node.name=="staticalloc") {
val struct = node.type.subType!!
TODO("symboltable alloc and initialize static struct ${struct.scopedNameString}")
}
null
}
else -> null // node is not present in the ST

View File

@@ -46,8 +46,12 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
is PtBuiltinFunctionCall -> {
val str = if(node.void) "void " else ""
str + node.name + "()"
if(node.name=="staticalloc") {
node.type.subType!!.scopedNameString+"()"
} else {
val str = if (node.void) "void " else ""
str + node.name + "()"
}
}
is PtContainmentCheck -> "in"
is PtFunctionCall -> {