On Fri, Jul 7, 2023 at 1:23 AM Krister Walfridsson via Gcc
<gcc@gcc.gnu.org> 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

Reply via email to