On Wed, Jun 28, 2023 at 5:47 PM Michael Matz via Gcc <gcc@gcc.gnu.org> wrote:
>
> Hello,
>
> On Wed, 28 Jun 2023, Krister Walfridsson via Gcc wrote:
>
> > Type safety
> > -----------
> > Some transformations treat 1-bit types as a synonym of _Bool and mix the 
> > types
> > in expressions, such as:
> >
> >   <unnamed-unsigned:1> _2;
> >   _Bool _3;
> >   _Bool _4;
> >   ...
> >   _4 = _2 ^ _3;
> >
> > and similarly mixing _Bool and enum
> >
> >   enum E:bool { E0, E1 };
> >
> > in one operation.
> >
> > I had expected this to be invalid... What are the type safety rules in the
> > GIMPLE IR?
>
> Type safety in gimple is defined in terms of type compatiblity, with
> _many_ exceptions for specific types of statements.  Generally stuff is
> verified in verify_gimple_seq., in this case of a binary assign statement
> in verify_gimple_assign_binary.  As you can see there the normal rules for
> bread-and-butter binary assigns is simply that RHS, LHS1 and LHS2 must
> all be type-compatible.
>
> T1 and T2 are compatible if conversions from T1 to T2 are useless and
> conversion from T2 to T1 are also useless.  (types_compatible_p)  The meat
> for that is all in gimple-expr.cc:useless_type_conversion_p.  For this
> specific case again we have:
>
>       /* Preserve conversions to/from BOOLEAN_TYPE if types are not
>          of precision one.  */
>       if (((TREE_CODE (inner_type) == BOOLEAN_TYPE)
>            != (TREE_CODE (outer_type) == BOOLEAN_TYPE))
>           && TYPE_PRECISION (outer_type) != 1)
>         return false;
>
> So, yes, booleans and 1-bit types can be compatible (under certain other
> conditions, see the function).
>
> > Somewhat related, gcc.c-torture/compile/pr96796.c performs a 
> > VIEW_CONVERT_EXPR
> > from
> >
> >   struct S1 {
> >     long f3;
> >     char f4;
> >   } g_3_4;
> >
> > to an int
> >
> >   p_51_9 = VIEW_CONVERT_EXPR<int>(g_3_4);
> >
> > That must be wrong?
>
> VIEW_CONVERT_EXPR is _very_ generous.  See
> verify_types_in_gimple_reference:

Yep.  In general these cases should rather use a BIT_FIELD_REF to select
a same sized subpart and only then do the rvalue conversion.  But as Micha says
below making the IL stricter isn't an easy task.

>       if (TREE_CODE (expr) == VIEW_CONVERT_EXPR)
>         {
>           /* For VIEW_CONVERT_EXPRs which are allowed here too, we only check
>              that their operand is not a register an invariant when
>              requiring an lvalue (this usually means there is a SRA or IPA-SRA
>              bug).  Otherwise there is nothing to verify, gross mismatches at
>              most invoke undefined behavior.  */
>           if (require_lvalue
>               && (is_gimple_reg (op) || is_gimple_min_invariant (op)))
>             {
>               error ("conversion of %qs on the left hand side of %qs",
>                      get_tree_code_name (TREE_CODE (op)), code_name);
>               debug_generic_stmt (expr);
>               return true;
>             }
>           else if (is_gimple_reg (op)
>                    && TYPE_SIZE (TREE_TYPE (expr)) != TYPE_SIZE (TREE_TYPE 
> (op)))
>             {
>               error ("conversion of register to a different size in %qs",
>                      code_name);
>               debug_generic_stmt (expr);
>               return true;
>             }
>         }
>
> Here the operand is not a register (but a global memory object), so
> everything goes.
>
> It should be said that over the years gimples type system became stricter
> and stricter, but it started as mostly everything-goes, so making it
> stricter is a bumpy road that isn't fully travelled yet, because checking
> types often results in testcase regressions :-)
>
> > Semantics of <signed-boolean:32>
> > --------------------------------
> > "Wide" Booleans, such as <signed-boolean:32>, seems to allow more values 
> > than
> > 0 and 1. For example, I've seen some IR operations like:
> >
> >   _66 = _16 ? _Literal (<signed-boolean:32>) -1 : 0;
> >
> > But I guess there must be some semantic difference between
> > <signed-boolean:32> and a 32-bit int, otherwise the wide Boolean type
> > would not be needed... So what are the difference?
>
> See above, normally conversions to booleans that are wider than 1 bit are
> _not_ useless (because they require booleanization to true/false).  In the
> above case the not-useless cast is within a COND_EXPR, so it's quite
> possible that the gimplifier didn't look hard enough to split this out
> into a proper conversion statement.  (The verifier doesn't look inside
> the expressions of the COND_EXPR, so also doesn't catch this one)

Note the above is GIMPLE FE syntax for a constant of a specific type,
a regular dump would just show _16 ? -1 : 0;

The thing with signed bools is that the two relevant values are -1 (true)
and 0 (false), those are used for vector bool components where we also
need them to be of wider type (32bits in this case).

Richard.

> If that turns out to be true and the above still happens when -1 is
> replaced by (say) 42, then it might be possible to construct a
> wrong-code testcase based on the fact that _66 as boolean should contain
> only two observable values (true/false), but could then contain 42.  OTOH,
> it might also not be possible to create such testcase, namely when GCC is
> internally too conservative in handling wide bools :-)  In that case we
> probably have a missed optimization somewhere, which when implemented
> would enable construction of such wrong-code testcase ;)
>
> (I'm saying that -1 should be replaced by something else for a wrong-code
> testcase, because -1 is special and could justifieably be special-cased in
> GCC: -1 is the proper arithmetic value for a signed boolean that is
> "true").
>
>
> Ciao,
> Michael.

Reply via email to