Types

LLVM has a rich type-system that is used to describe the types of values in a program. LLVM.jl attempts to reconstruct as much of this type-system as possible in Julia, beyond the abstractions that the C API provides (where types are opaque objects).

Each type object, represented by a subtype of LLVMType, supports a few property functions:

  • issized: whether the type has a fixed size (e.g. Int32).
  • context: the context in which the type was created.
  • eltype: the element type of the type (if applicable, e.g., for arrays and vectors).

The types describing LLVM types are not exported by default, so always need to be prefixed by the LLVM module name.

Integer types

Integer types are subtypes of the LLVM.IntegerType abstract type. They can be created using explicit constructors that encode the bit-width, or using constructors that take a width argument:

julia> LLVM.Int1Type()
i1

julia> LLVM.IntType(32)
i32

It is possible to query the bit-width of an integer type using the width property:

julia> LLVM.Int32Type().width
32

Floating-point types

Floating-point types are subtypes of the LLVM.FloatingPointType abstract type. They can only be constructed using explicitly named constructors:

julia> LLVM.HalfType()
half

julia> LLVM.BFloatType()
bfloat

Function types

Function types are used to create functions, and encode both the return type and the argument types, which can be queried using respectively the return_type and parameters properties. Types cannot be changed, so the parameters are a read-only view.

julia> LLVM.FunctionType(LLVM.Int1Type())
i1 ()

julia> ft = LLVM.FunctionType(LLVM.Int1Type(), [LLVM.FloatType()])
i1 (float)

julia> ft.return_type
i1

julia> collect(ft.parameters)
1-element Vector{LLVMType}:
 float

To create vararg functions, the vararg keyword argument to the FunctionType constructor can be used. This property can be queried using the isvararg function.

Pointer types

Pointer types are represented by the LLVM.PointerType type. Since LLVM 17, all pointers are opaque, i.e., they don't have an element type. Older versions of LLVM also support typed pointers, whose element type is available as the element_type property:

julia> supports_typed_pointers()
true

julia> ty = LLVM.PointerType(LLVM.Int1Type())
i1*

julia> ty.element_type
i1

When constructing a pointer type, you can also set the address space, and query it back using the addrspace property:

julia> ty = LLVM.PointerType(LLVM.Int1Type(), 1)
ptr addrspace(1)

julia> ty.addrspace
1

Array types

LLVM arrays represent a fixed-size, homoeneous collection of elements. They can be created using the LLVM.ArrayType constructor:

julia> ty = LLVM.ArrayType(LLVM.Int1Type(), 8)
[8 x i1]

julia> ty.length
8

julia> ty.element_type
i1

Vector types

LLVM vectors are similar, but mostly used for SIMD operations:

julia> ty = LLVM.VectorType(LLVM.Int1Type(), 8)
<8 x i1>

julia> ty.length
8

julia> ty.element_type
i1

Structure types

Structure types are used to represent a collection of elements of different types. They can be created using the LLVM.StructType constructor:

julia> ty = LLVM.StructType([LLVM.Int32Type(), LLVM.FloatType()])
{ i32, float }

It is also possible to start with an empty type and add elements to it:

julia> ty = LLVM.StructType("MyStruct")
%MyStruct = type opaque

julia> elements!(ty, [LLVM.Int32Type(), LLVM.FloatType()])

julia> ty
%MyStruct = type { i32, float }

Structure types support a number of queries:

  • ty.name: the name of the structure type.
  • ty.elements: the element types of the structure type, as a read-only view (use elements! to set the body of an opaque structure type).
  • ispacked: whether the structure is packed.
  • isopaque: whether the structure is opaque.
  • isempty: whether the structure is empty.

Other types

There are a few other types that do not fit in a specific category:

  • LLVM.VoidType: the void type.
  • LLVM.LabelType: the label type.
  • LLVM.MetadataType: the metadata type.
  • LLVM.TokenType: the token type.

Named types

Although uncommon, it is possible to look up the named types that are registered in a context using its types property. LLVM does not support iterating these types, but the property can be used to check whether a type is registered in a context:

julia> ctx = context();

julia> haskey(ctx.types, "Foo")
false

julia> ty = LLVM.StructType("Foo")
%Foo = type opaque

julia> haskey(ctx.types, "Foo")
true

julia> ctx.types["Foo"]
%Foo = type opaque