https://gcc.gnu.org/g:421b31578d4217481f34bf306e67f9365ed65190
commit 421b31578d4217481f34bf306e67f9365ed65190 Author: Alexandre Oliva <ol...@adacore.com> Date: Thu Jul 17 17:54:00 2025 -0300 [hardbool] implement OP=, ++ and --, volatile and atomics hardbools didn't behave quite like bools when incremented, decremented, or otherwise modified using their previous value, as in += et al. Fix that. Also fix some checking errors that come up when using qualified base types, and implement sane semantics for increments and decrements of volatile hardbools. While at that, fix an (apparent?) double-evaluation when postdecrementing regular booleans. for gcc/c-family/ChangeLog * c-attribs.cc (handle_hardbool_attribute): Create distinct enumeration types, with structural equality. Handle base type qualifiers. * c-common.cc (boolean_increment): Avoid double evaluation on postdecrement. for gcc/c/ChangeLog * c-tree.h (C_BOOLEAN_TYPE_P): Cover hardbools as well. * c-typeck.cc (convert_lvalue_to_rvalue): New overload and wrapper. (build_atomic_assign, build_modify_expr): Use it. (build_asm_expr, handle_omp-array_sections_1): Simplify with it. (build_unary_op): Handle hardbools. for gcc/testsuite/ChangeLog * gcc.dg/torture/hardbool-ai.c: New. * gcc.dg/torture/hardbool-vi.c: New. * gcc.dg/torture/hardbool.c: Handle NO_BITFIELDS. (add1, preinc, postinc, sub1, predec, postdec): New. (main): Exercise them. Diff: --- gcc/c-family/c-attribs.cc | 13 +++-- gcc/c-family/c-common.cc | 6 +-- gcc/c/c-tree.h | 3 +- gcc/c/c-typeck.cc | 83 ++++++++++++++++++++++++------ gcc/testsuite/gcc.dg/torture/hardbool-ai.c | 7 +++ gcc/testsuite/gcc.dg/torture/hardbool-vi.c | 5 ++ gcc/testsuite/gcc.dg/torture/hardbool.c | 68 +++++++++++++++++++++++- 7 files changed, 162 insertions(+), 23 deletions(-) diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index 1f4a0df12051..e7500ca2beb4 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -1128,11 +1128,12 @@ handle_hardbool_attribute (tree *node, tree name, tree args, } tree orig = *node; - *node = build_duplicate_type (orig); + tree unqual = build_qualified_type (orig, TYPE_UNQUALIFIED); + *node = build_distinct_type_copy (unqual); TREE_SET_CODE (*node, ENUMERAL_TYPE); ENUM_UNDERLYING_TYPE (*node) = orig; - TYPE_CANONICAL (*node) = TYPE_CANONICAL (orig); + SET_TYPE_STRUCTURAL_EQUALITY (*node); tree false_value; if (args) @@ -1191,7 +1192,13 @@ handle_hardbool_attribute (tree *node, tree name, tree args, gcc_checking_assert (!TYPE_CACHED_VALUES_P (*node)); TYPE_VALUES (*node) = values; - TYPE_NAME (*node) = orig; + TYPE_NAME (*node) = unqual; + + if (TYPE_QUALS (orig) != TYPE_QUALS (*node)) + { + *node = build_qualified_type (*node, TYPE_QUALS (orig)); + TYPE_NAME (*node) = orig; + } return NULL_TREE; } diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index 362dc506f785..1382983a0d03 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -5534,11 +5534,11 @@ boolean_increment (enum tree_code code, tree arg) invert_truthvalue_loc (input_location, arg)); break; case POSTDECREMENT_EXPR: - val = build2 (MODIFY_EXPR, TREE_TYPE (arg), arg, - invert_truthvalue_loc (input_location, arg)); + val = arg; arg = save_expr (arg); + val = build2 (MODIFY_EXPR, TREE_TYPE (val), val, + invert_truthvalue_loc (input_location, arg)); val = build2 (COMPOUND_EXPR, TREE_TYPE (arg), val, arg); - val = build2 (COMPOUND_EXPR, TREE_TYPE (arg), arg, val); break; default: gcc_unreachable (); diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 364f51df58c9..daf79f722760 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -163,7 +163,8 @@ along with GCC; see the file COPYING3. If not see (TREE_CODE (TYPE) == BOOLEAN_TYPE \ || (TREE_CODE (TYPE) == ENUMERAL_TYPE \ && ENUM_UNDERLYING_TYPE (TYPE) != NULL_TREE \ - && TREE_CODE (ENUM_UNDERLYING_TYPE (TYPE)) == BOOLEAN_TYPE)) + && (TREE_CODE (ENUM_UNDERLYING_TYPE (TYPE)) == BOOLEAN_TYPE \ + || c_hardbool_type_attr (TYPE)))) /* Record parser information about an expression that is irrelevant for code generation alongside a tree representing its value. */ diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index e022e61c6b19..f6fa288b6ab2 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -2648,6 +2648,20 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp, return exp; } +/* Wrapper for the overload above, same arguments but for tree rather than + c_expr. This is important for hardbools to decay to bools. */ + +static inline tree +convert_lvalue_to_rvalue (location_t loc, tree val, + bool convert_p, bool read_p, bool for_init = false) +{ + struct c_expr expr; + memset (&expr, 0, sizeof (expr)); + expr.value = val; + expr = convert_lvalue_to_rvalue (loc, expr, convert_p, read_p, for_init); + return expr.value; +} + /* EXP is an expression of integer type. Apply the integer promotions to it and return the promoted value. */ @@ -5271,7 +5285,9 @@ cas_loop: /* newval = old + val; */ if (rhs_type != rhs_semantic_type) val = build1 (EXCESS_PRECISION_EXPR, nonatomic_rhs_semantic_type, val); - rhs = build_binary_op (loc, modifycode, old, val, true); + rhs = build_binary_op (loc, modifycode, + convert_lvalue_to_rvalue (loc, old, true, true), + val, true); if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR) { tree eptype = TREE_TYPE (rhs); @@ -5727,7 +5743,48 @@ build_unary_op (location_t location, enum tree_code code, tree xarg, goto return_build_unary_op; } - if (C_BOOLEAN_TYPE_P (TREE_TYPE (arg))) + tree true_res; + if (c_hardbool_type_attr (TREE_TYPE (arg), NULL, &true_res)) + { + tree larg = stabilize_reference (arg); + tree sarg = save_expr (larg); + switch (code) + { + case PREINCREMENT_EXPR: + val = build2 (MODIFY_EXPR, TREE_TYPE (larg), larg, true_res); + val = build2 (COMPOUND_EXPR, TREE_TYPE (larg), sarg, val); + break; + case POSTINCREMENT_EXPR: + val = build2 (MODIFY_EXPR, TREE_TYPE (larg), larg, true_res); + val = build2 (COMPOUND_EXPR, TREE_TYPE (larg), val, sarg); + val = build2 (COMPOUND_EXPR, TREE_TYPE (larg), sarg, val); + break; + case PREDECREMENT_EXPR: + { + tree rarg = convert_lvalue_to_rvalue (location, sarg, + true, true); + rarg = invert_truthvalue_loc (location, rarg); + rarg = convert (TREE_TYPE (sarg), rarg); + val = build2 (MODIFY_EXPR, TREE_TYPE (larg), larg, rarg); + } + break; + case POSTDECREMENT_EXPR: + { + tree rarg = convert_lvalue_to_rvalue (location, sarg, + true, true); + rarg = invert_truthvalue_loc (location, rarg); + tree iarg = convert (TREE_TYPE (larg), rarg); + val = build2 (MODIFY_EXPR, TREE_TYPE (larg), larg, iarg); + val = build2 (COMPOUND_EXPR, TREE_TYPE (larg), val, sarg); + val = build2 (COMPOUND_EXPR, TREE_TYPE (larg), sarg, val); + } + break; + default: + gcc_unreachable (); + } + TREE_SIDE_EFFECTS (val) = 1; + } + else if (C_BOOLEAN_TYPE_P (TREE_TYPE (arg))) val = boolean_increment (code, arg); else val = build2 (code, TREE_TYPE (arg), arg, inc); @@ -7335,8 +7392,10 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype, clear_decl_read = true; } - newrhs = build_binary_op (location, - modifycode, lhs, newrhs, true); + newrhs = build_binary_op (location, modifycode, + convert_lvalue_to_rvalue (location, lhs, + true, true), + newrhs, true); if (clear_decl_read) DECL_READ_P (lhs) = 0; @@ -12725,11 +12784,9 @@ build_asm_expr (location_t loc, tree string, tree outputs, tree inputs, } else { - struct c_expr expr; - memset (&expr, 0, sizeof (expr)); - expr.value = input; - expr = convert_lvalue_to_rvalue (loc, expr, true, false); - input = c_fully_fold (expr.value, false, NULL); + input = c_fully_fold (convert_lvalue_to_rvalue (loc, input, + true, false), + false, NULL); if (input != error_mark_node && VOID_TYPE_P (TREE_TYPE (input))) { @@ -15349,12 +15406,8 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types, /* If the array section is pointer based and the pointer itself is _Atomic qualified, we need to atomically load the pointer. */ - c_expr expr; - memset (&expr, 0, sizeof (expr)); - expr.value = ret; - expr = convert_lvalue_to_rvalue (OMP_CLAUSE_LOCATION (c), - expr, false, false); - ret = expr.value; + ret = convert_lvalue_to_rvalue (OMP_CLAUSE_LOCATION (c), + ret, false, false); } return ret; } diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ai.c b/gcc/testsuite/gcc.dg/torture/hardbool-ai.c new file mode 100644 index 000000000000..97569a6de6da --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ai.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ + +#define basetype _Atomic int + +#define NO_BITFIELDS 1 + +#include "hardbool.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-vi.c b/gcc/testsuite/gcc.dg/torture/hardbool-vi.c new file mode 100644 index 000000000000..898d395c5c52 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-vi.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ + +#define basetype volatile int + +#include "hardbool.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool.c b/gcc/testsuite/gcc.dg/torture/hardbool.c index 01684952a2a9..ed0c5988d2be 100644 --- a/gcc/testsuite/gcc.dg/torture/hardbool.c +++ b/gcc/testsuite/gcc.dg/torture/hardbool.c @@ -21,8 +21,12 @@ typedef unsigned char __attribute__ ((__hardbool__ (1, 0))) zbool; struct hs { hbool a[2]; +#ifndef NO_BITFIELDS hbool x:2; hbool y:5; +#else + hbool x, y; +#endif zbool z:1; }; @@ -57,6 +61,30 @@ int ghs(hbool s) { int t = (hbool)2; +hbool add1(hbool *s) { + return *s += 1; +} + +hbool preinc(hbool *s) { + return ++*s; +} + +hbool postinc(hbool *s) { + return (*s)++; +} + +hbool sub1(hbool *s) { + return *s -= 1; +} + +hbool predec(hbool *s) { + return --*s; +} + +hbool postdec(hbool *s) { + return (*s)--; +} + void check_pfalse (hbool *p) { assert (!*p); @@ -114,5 +142,43 @@ int main () { check_vtrue (h2 (2)); check_vtrue (h2 (1)); check_vfalse (h2 (0)); -} + hbool v; + v = 0; + check_vtrue (add1 (&v)); + assert (v); + v = 0; + check_vtrue (preinc (&v)); + assert (v); + v = 0; + check_vfalse (postinc (&v)); + assert (v); + v = 0; + check_vtrue (sub1 (&v)); + assert (v); + v = 0; + check_vtrue (predec (&v)); + assert (v); + v = 0; + check_vfalse (postdec (&v)); + assert (v); + + v = 1; + check_vtrue (add1 (&v)); + assert (v); + v = 1; + check_vtrue (preinc (&v)); + assert (v); + v = 1; + check_vtrue (postinc (&v)); + assert (v); + v = 1; + check_vfalse (sub1 (&v)); + assert (!v); + v = 1; + check_vfalse (predec (&v)); + assert (!v); + v = 1; + check_vtrue (postdec (&v)); + assert (!v); +}