Using gcroot with value types

Hi,

Talin wrote:

I'm a little confused as to the rules for the arguments to llvm.gcroot,
which says it must be a pointer alloca. I'm not sure whether that means it
must be an alloca (which is always a pointer by definition) or an alloca
*of* a pointer.

I'm pretty sure it should be "alloca of a pointer", as the first argument
of llvm.gcroot has type i8**.

However, Garbage Collection with LLVM — LLVM 16.0.0git documentation, isn't
completely clear on this:

"The first argument must be a value referring to an alloca instruction or
a bitcast of an alloca."

This last sentence seems to rule out passing GEPs on alloca's to llvm.gcroot.

What I am trying to figure out is how to declare roots contained in value
types. Suppose for example I have a small struct which contains several
garbage-collectable references. This struct is a local variable, so it's
contained in a block of memory which has been allocated via alloca. Now, I
don't want to add the struct itself as a root, since it isn't allocated on
the heap. But I want to declare the fields within the struct - which are
GEPs from the alloca pointer - as roots, since they are references to
objects on the heap.

As reasonable as the above sounds, I don't think you can mark roots
contained in structs. But if I'm wrong I'd love to know :wink: Perhaps you
can ask the LDC devs how/if they handle this case, as it seems you can
allocate a class instance on the stack in D.

Regards,
Paul

Hi,

Talin wrote:

I’m a little confused as to the rules for the arguments to llvm.gcroot,
which says it must be a pointer alloca. I’m not sure whether that means it
must be an alloca (which is always a pointer by definition) or an alloca
of a pointer.

I’m pretty sure it should be “alloca of a pointer”, as the first argument
of llvm.gcroot has type i8**.

However, http://llvm.org/docs/GarbageCollection.html#gcroot, isn’t
completely clear on this:

“The first argument must be a value referring to an alloca instruction or
a bitcast of an alloca.”

This last sentence seems to rule out passing GEPs on alloca’s to llvm.gcroot.

What I am trying to figure out is how to declare roots contained in value
types. Suppose for example I have a small struct which contains several
garbage-collectable references. This struct is a local variable, so it’s
contained in a block of memory which has been allocated via alloca. Now, I
don’t want to add the struct itself as a root, since it isn’t allocated on
the heap. But I want to declare the fields within the struct - which are
GEPs from the alloca pointer - as roots, since they are references to
objects on the heap.

As reasonable as the above sounds, I don’t think you can mark roots
contained in structs. But if I’m wrong I’d love to know :wink: Perhaps you
can ask the LDC devs how/if they handle this case, as it seems you can
allocate a class instance on the stack in D.

Ugh. If all roots have to be singleton pointers, not embedded within a larger structure, then I’m not sure that what I want to do is even possible.

In principle you could probably mark the alloca’s of your value structs as gcroots. Then during collection you would have to make sure you recognize the value types so you don’t move/deallocate/… them during a collection. That would still give you the option to trace all live objects by simply traversing the fields of a value struct. You would only have to do this for value structs (indirectly) containing references. Does that make sense?

BTW, I checked the LDC sources, they don’t use LLVM’s GC support, but use their own GC stuff in D, so no clues there…

Regards,
Paul

Hi,

Talin wrote:

I’m a little confused as to the rules for the arguments to llvm.gcroot,
which says it must be a pointer alloca. I’m not sure whether that means it
must be an alloca (which is always a pointer by definition) or an alloca
of a pointer.

I’m pretty sure it should be “alloca of a pointer”, as the first argument
of llvm.gcroot has type i8**.

However, http://llvm.org/docs/GarbageCollection.html#gcroot, isn’t
completely clear on this:

“The first argument must be a value referring to an alloca instruction or
a bitcast of an alloca.”

This last sentence seems to rule out passing GEPs on alloca’s to llvm.gcroot.

What I am trying to figure out is how to declare roots contained in value
types. Suppose for example I have a small struct which contains several
garbage-collectable references. This struct is a local variable, so it’s
contained in a block of memory which has been allocated via alloca. Now, I
don’t want to add the struct itself as a root, since it isn’t allocated on
the heap. But I want to declare the fields within the struct - which are
GEPs from the alloca pointer - as roots, since they are references to
objects on the heap.

As reasonable as the above sounds, I don’t think you can mark roots
contained in structs. But if I’m wrong I’d love to know :wink: Perhaps you
can ask the LDC devs how/if they handle this case, as it seems you can
allocate a class instance on the stack in D.

Ugh. If all roots have to be singleton pointers, not embedded within a larger structure, then I’m not sure that what I want to do is even possible.

In principle you could probably mark the alloca’s of your value structs as gcroots. Then during collection you would have to make sure you recognize the value types so you don’t move/deallocate/… them during a collection. That would still give you the option to trace all live objects by simply traversing the fields of a value struct. You would only have to do this for value structs (indirectly) containing references. Does that make sense?

Unfortunately, when I try to do this, the function verifier reports the function as broken. It requires that the argument to llvm.gcroot be a pointer to an alloca of a pointer. I can’t fool it with casts.

Oops, I wasn’t specific enough. Marking an alloca of a value type indeed won’t work, as you noticed. Put differently, the idea is that value types and heap types are treated the same w.r.t. marking them with gcroot, while the former is allocated using alloca and the latter with your GC allocation routine. It’s only during collection that you have to make sure not to collect value types.

// Some value type
%struct.V = type { i32, float, i8* }

// Allocate space for a pointer to a V instance, which we mark as GC root
%v = alloca %struct.V*, align 8 ; <%struct.V**> [#uses=2]
%0 = bitcast %struct.V** %v to i8** ; <i8**> [#uses=1]
call void @llvm.gcroot(i8** %0, i8* null)

// Allocate the V instance on the stack and store it’s address in the root
%1 = alloca %struct.V, align 4 ; <%struct.V*> [#uses=3]
store %struct.V* %1, %struct.V** %v, align 8

Paul

Hi,

Talin wrote:

I’m a little confused as to the rules for the arguments to llvm.gcroot,
which says it must be a pointer alloca. I’m not sure whether that means it
must be an alloca (which is always a pointer by definition) or an alloca
of a pointer.

I’m pretty sure it should be “alloca of a pointer”, as the first argument
of llvm.gcroot has type i8**.

However, http://llvm.org/docs/GarbageCollection.html#gcroot, isn’t
completely clear on this:

“The first argument must be a value referring to an alloca instruction or
a bitcast of an alloca.”

This last sentence seems to rule out passing GEPs on alloca’s to llvm.gcroot.

What I am trying to figure out is how to declare roots contained in value
types. Suppose for example I have a small struct which contains several
garbage-collectable references. This struct is a local variable, so it’s
contained in a block of memory which has been allocated via alloca. Now, I
don’t want to add the struct itself as a root, since it isn’t allocated on
the heap. But I want to declare the fields within the struct - which are
GEPs from the alloca pointer - as roots, since they are references to
objects on the heap.

As reasonable as the above sounds, I don’t think you can mark roots
contained in structs. But if I’m wrong I’d love to know :wink: Perhaps you
can ask the LDC devs how/if they handle this case, as it seems you can
allocate a class instance on the stack in D.

Ugh. If all roots have to be singleton pointers, not embedded within a larger structure, then I’m not sure that what I want to do is even possible.

In principle you could probably mark the alloca’s of your value structs as gcroots. Then during collection you would have to make sure you recognize the value types so you don’t move/deallocate/… them during a collection. That would still give you the option to trace all live objects by simply traversing the fields of a value struct. You would only have to do this for value structs (indirectly) containing references. Does that make sense?

Unfortunately, when I try to do this, the function verifier reports the function as broken. It requires that the argument to llvm.gcroot be a pointer to an alloca of a pointer. I can’t fool it with casts.

Oops, I wasn’t specific enough. Marking an alloca of a value type indeed won’t work, as you noticed. Put differently, the idea is that value types and heap types are treated the same w.r.t. marking them with gcroot, while the former is allocated using alloca and the latter with your GC allocation routine. It’s only during collection that you have to make sure not to collect value types.

// Some value type
%struct.V = type { i32, float, i8* }

// Allocate space for a pointer to a V instance, which we mark as GC root
%v = alloca %struct.V*, align 8 ; <%struct.V**> [#uses=2]
%0 = bitcast %struct.V** %v to i8** ; <i8**> [#uses=1]
call void @llvm.gcroot(i8** %0, i8* null)

// Allocate the V instance on the stack and store it’s address in the root
%1 = alloca %struct.V, align 4 ; <%struct.V*> [#uses=3]
store %struct.V* %1, %struct.V** %v, align 8

I guess that will work, although I’m not happy about having to allocate an extra pointer for every value type. But that will work for now.

The collection part is trivial, since in my scheme the metadata argument to gcroot is always null for reference types (since they are tagged), and non-null for value types (since they are not.)