[RFC] Add nopoison attribute/metadata

This is the case right now, but I think we would like to expose delayed (poison-style) UB in Rust at some point. Requests for this appear fairly regularly, e.g. as a way to safely expose fast-math (have a new type that allows poison, and a safe method to convert to regular float types that calls freeze), and to deal with vectors where some lanes are poison.

So from a Rust perspective I am concerned that this proposal will lock us in a corner where we cannot have these features in the language.

Given that undef is slated for removal (in my understanding), a transformation involving undef is IMO not a great justification for such a fundamental change.

In my view, poison is the only “invalid value” LLVM needs (no need for undef, no need for a separate concept of “uninitialized memory”), so it stands for uninitialized memory and padding.

It seems to me like all these are solved by having some type that can hold arbitrary data, everything representable in memory. If you have undef/uninit in memory but not as an SSA value, then a load-store roundtrip is not a NOP so you need to carefully argue why removing a redundant store is okay. Also given that the provenance problem exists, I don’t see how “no poison in memory” actually helps. Whatever solution later materializes for provenance will likely also deal with poison in memory, and then we can avoid limiting frontends to the “nopoison” subset of LLVM.

I think the fundamental problem is: Having memory contents that cannot be represented as SSA values, or vice versa, will always introduce friction. The principled fix is to make sure that both memory and SSA values can represent the same things, to make seamless roundtrips possible.

That’s why I think a proper solution requires either a byte type, or making iN explicitly able to hold all in-memory-data in a lossless way and add some sort of attribute for “no provenance, no poison in any bit” (as in this proposal).

The Rust frontend needs to be able to store everything in allocas – if I can have a variable x: T, I can always create a reference to it and pass that out to other code, which requires x to be stored into an alloca. So if we want Rust code to be able to have a vector with some lanes being poison, we need to be able to store that to memory. More broadly speaking, a value that is UB to store in memory is a concept that makes no sense for Rust as all data only exists in memory. Local variables are merely pointers to somewhere in memory where the actual data is stored.


This is unrelated to this discussion, but note that storing back a just-loaded value is still a data race if there are concurrent accesses, so introducing such stores in a concurrent program can introduce UB.

1 Like