On Fri, Jul 7, 2023 at 1:23 AM Krister Walfridsson via Gcc <[email protected]> wrote: > > I have implemented support for uninitialized memory in my translation > validator. But I am not sure how well this corresponds to the GIMPLE > semantics, so I have some questions... > > My implementation tracks uninitialized bits. Use of uninitialized bits is > in general treated as UB (for example, `x + y` is UB if `x` or `y` has any > uninitialized bits), but there are a few exceptions: > > * load/store: It is always OK to load/store values having uninitialized > bits. > * Phi nodes: Phi nodes propagates uninitialized bits.
definitely, I think plain copies would be OK as well > * selection: Instructions that are selecting an element (COND_EXPR, > VEC_PERM_EXPR, etc.) may select between values having uninitialized > bits, and the resulting value may have uninitialized bits. But the > condition/mask must not have uninitialized bits. > * Extraction: Instructions that are extracting bits (BIT_FIELD_REF etc.) > may have uninitialized bits in both the input/output. > * Insertion: Instructions that are constructing/inserting values > (COMPLEX_EXPR, etc.) may have uninitialized bits in both the > input/output. Generally I think "moving" uninitialized bits in full or in part is OK. Operating on them, including comparing them (with themselves) is eventually asking for troubles. > All other use of values having uninitialized bits are considered UB. > > Does this behavior make sense? > > The above seems to work fine so far, with one exception that can be seen > in gcc.c-torture/execute/pr108498-1.c. The test has an uninitialized bit > field > > unsigned char c6:1, c7:1, c8:1, c9:3, c10:1; > > which is written to as > > x.c6 = 0; > x.c7 = 0; > x.c8 = 0; > x.c9 = 7; > > The store merging pass changes this to > > _71 = MEM <unsigned char> [(struct C *)&x + 8B]; > _42 = _71 & 128; > _45 = _42 | 56; > > and the translation validator is now complaining that the pass has > introduced UB that was not in the original IR (because the most > significant bit in _71 is uninitialized when passed to BIT_AND_EXPR). > > I could solve this by allowing uninitialized bits in BIT_AND_EXPR and > BIT_OR_EXP, and propagating each bit according to > > * `0 & uninit` is an initialized `0` > * `1 & uninit` is uninitialized > * `0 | uninit` is uninitialized > * `1 | uninit` is an initialized `1` > > Is that the correct GIMPLE semantics? I think the above "moves" the uninitialized MSB from memory to _45 so that's OK. Some "operations" like & 0 or & 1 give either defined values or take the uninitialized bit unmodified (thus "move"). I think both kinds are OK. Likewise + 0 or * 0/1 would be OK. What's not OK is operations that use an (fully?) uninitialized value twice, like x - x when x is uninitialized. I think we want that, as soon as the uninitialized value becomes "part" of a then partly initialized value, it's value is "fixed". With things like _Complex or vector the situation is a bit of a grey area since we have ways to operate on elements. Note that when we for example use a BIT_FIELD_REF to extract the MSB from _42 above the value would be again fully undefined. I hope this makes some sense, it's a tricky area. Richard. > > /Krister
