Struct Representation and ABI
Structs are currently used mostly through named module types, most visibly the
компл type from the complex-number module. At AST level a struct is
TStructType, optionally wrapped in TNamedType so user-facing diagnostics and
printers can keep the domain name:
<named компл <struct (re f64) (im f64)>>
IR Type
FromAstType(TStructType) maps to an IR Struct type whose fields are the
lowered field types in declaration order. TTypeTable interns struct layouts
by the vector of field type ids:
struct { f64; f64 } ; complex number payload
Struct size is the sum of field sizes in the IR type table. Field offsets are computed by summing sizes of all previous fields; there is no separate runtime header or field-name table.
In-Memory Layout
When lowering needs an addressable struct value, it emits salloc(size) and
writes fields into that temporary buffer. Field access computes:
fieldPtr = objectAddress + fieldByteOffset
For scalar fields, reads use lde and writes use ste. For nested struct
fields, field access returns the field pointer and assignments use copy
because the value may be address-backed rather than an SSA scalar.
Struct values inside arrays are stored inline in the flat array buffer. An array access to a struct element returns a pointer to the element so callers can copy from or write into that storage.
LLVM ABI
The LLVM backend models struct arguments and return values as ordinary LLVM struct values, not hidden out-parameters:
define { double, double } @make_complex(...)
define void @use_complex({ double, double } %z)
This is the external ABI seen by LLVM functions. The IR lowering may still represent an expression as a pointer to a temporary struct buffer. Codegen bridges this mismatch at call and return boundaries:
| Boundary | Codegen behavior |
|---|---|
| struct argument | if the IR operand is a pointer, load the LLVM struct value before call |
| struct return | if ret receives a pointer but LLVM expects a struct, load before ret |
| store into struct local | if destination is a struct alloca and source is a pointer, emit memcpy |
copy into pointer |
if source is already a struct LLVM value, store it; otherwise memcpy |
So the high-level convention is value ABI for LLVM, with address-backed temporaries as an implementation detail inside lowering.
Function Parameters and Reference Parameters
Plain struct parameters are passed by value in LLVM. Mutating such a parameter inside the callee mutates the callee's local copy, not the caller's object.
Reference parameters (рез / арг рез) are lowered as pointer/reference
types. For a struct reference assignment, lowering copies the full struct into
the referenced destination:
addr = load reference parameter
copy addr, rhsStruct, sizeof(struct)
This is the mechanism used for рез компл-style APIs: the ABI carries an
address, and assignment writes the whole struct payload into the caller-owned
storage.
VM Notes
The VM has extra support for materializing struct temporaries and copying
struct payloads. StructStore copies a struct value into a local frame slot,
and call/return handling materializes struct temporaries when a struct value is
represented by an address in IR. This keeps VM behavior aligned with the LLVM
value ABI at the language level.