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);
+}

Reply via email to