When emitting code to an object file, pointers will be emitted as relocations. The deserialization code will ensure any object that pointed to one of these constants gets recreated and contains the right runtime pointer.
Otherwise, they will be emitted as literal constants.
To emit one of these objects, callliteral_pointer_val
. It'll handle tracking the Julia value and the LLVM global, ensuring they are valid both for the current runtime and after deserialization.
When emitted into the object file, these globals are stored as references in a largegvals
table. This allows the deserializer to reference them by index, and implement a custom manual mechanism similar to a Global Offset Table (GOT) to restore them.
Function pointers are handled similarly. They are stored as values in a largefvals
table. Like globals, this allows the deserializer to reference them by index.
Note thatextern
functions are handled separately, with names, via the usual symbol resolution mechanism in the linker.
Note too thatccall
functions are also handled separately, via a manual GOT and Procedure Linkage Table (PLT).
Values are passed around in ajl_cgval_t
struct. This represents an R-value, and includes enough information to determine how to assign or pass it somewhere.
They are created via one of the helper constructors, usually:mark_julia_type
(for immediate values) andmark_julia_slot
(for pointers to values).
The functionconvert_julia_type
can transform between any two types. It returns an R-value withcgval.typ
set totyp
. It'll cast the object to the requested representation, making heap boxes, allocating stack copies, and computing tagged unions as needed to change the representation.
By contrastupdate_julia_type
will changecgval.typ
totyp
, only if it can be done at zero-cost (i.e. without emitting any code).
Inferred union types may be stack allocated via a tagged type representation.
The primitive routines that need to be able to handle tagged unions are:
Everything else should be possible to handle in inference by using these primitives to implement union-splitting.
The representation of the tagged-union is as a pair of< void* union, byte selector >
. The selector is fixed-size asbyte & 0x7f
, and will union-tag the first 126 isbits. It records the one-based depth-first count into the type-union of the isbits objects inside. An index of zero indicates that theunion*
is actually a tagged heap-allocatedjl_value_t*
, and needs to be treated as normal for a boxed object rather than as a tagged union.
The high bit of the selector (byte & 0x80
) can be tested to determine if thevoid*
is actually a heap-allocated (jl_value_t*
) box, thus avoiding the cost of re-allocating a box, while maintaining the ability to efficiently handle union-splitting based on the low bits.
It is guaranteed thatbyte & 0x7f
is an exact test for the type, if the value can be represented by a tag – it will never be markedbyte = 0x80
. It is not necessary to also test the type-tag when testingisa
.
Theunion*
memory region may be allocated atany size. The only constraint is that it is big enough to contain the data currently specified byselector
. It might not be big enough to contain the union of all types that could be stored there according to the associated Union type field. Use appropriate care when copying.
Ajl_returninfo_t
object describes the calling convention details of any callable.
If any of the arguments or return type of a method can be represented unboxed, and the method is not varargs, it'll be given an optimized calling convention signature based on itsspecTypes
andrettype
fields.
The general principles are that:
The total logic for this is implemented byget_specsig_function
anddeserves_sret
.
Additionally, if the return type is a union, it may be returned as a pair of values (a pointer and a tag). If the union values can be stack-allocated, then sufficient space to store them will also be passed as a hidden first argument. It is up to the callee whether the returned pointer will point to this space, a boxed object, or even other constant memory.
Settings
This document was generated withDocumenter.jl version 1.8.0 onWednesday 9 July 2025. Using Julia version 1.11.6.