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: 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) 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.