Proposal: "Fragile" Global References

I’d like to be able to have “weak” references to global variables. Now, currently LLVM allows global values to have “weak” linkage type (and variants thereof), but this is a different meaning of the word “weak” - what I’m thinking of is similar to the concept of weak references in Java and other languages that support garbage collection, except that the “collection” I’m talking about is the dead global elimination pass. In other words, I’d like to be able to have a reference to a global variable that doesn’t prevent that global from being deleted by the dead global pass, but which retains that reference if there are other “strong” references to that global. If the global does get deleted, the “weak” reference is replaced with a NULL pointer.

To avoid confusion, I’ll use the term “fragile” or “tenuous” instead of “weak” to refer to these kinds of references. This “fragility” is a is not a property of the global, but rather a property of the pointer to that global.

An example of a use case for fragile references relates to generating reflection data. There are a lot of cases where one would like to be able to iterate at runtime over all of the classes or functions in a module or package. In order to do this, there has to be some table or other data structure that contains references to all of the class or function objects. However, the very existence of that table prevents the dead global elimination pass from removing classes and functions that aren’t needed. What we really want to have is the set of class or function objects that are left over after the dead globals have been eliminated.

A similar use case is for trace tables for static roots, and other kinds of metadata which are associated with a function or global.

Here’s how this could be implemented: A new type of constant expression “fragile(ptr)” would be introduced. This is simply a wrapper around a pointer, which returns the value of that pointer. The dead global elimination pass could be modified to look for instances of this expression type when traversing the constant structures. The pass would replace the argument of fragile() with NULL if the global value was deleted.

There would also be a new pass that would normalize all of the fragile references - removing the call to fragile() and replacing it with it’s contents. This would typically be run after all of the various global optimizations were finished. In addition, this pass could also remove array elements from arrays with appending linkage - that is, rather than replacing fragile(NULL) with NULL, it would simply delete the array element entirely.

I’d be curious to know if anyone else would find something like this useful.