public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/vendors/ARM/heads/morello)] c: Fixes for build_conditional_expr and __capability propagation
@ 2023-02-28 10:09 Alex Coplan
  0 siblings, 0 replies; only message in thread
From: Alex Coplan @ 2023-02-28 10:09 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:2a6819fecb5fdce883b682d7109f935e3922dab5

commit 2a6819fecb5fdce883b682d7109f935e3922dab5
Author: Alex Coplan <alex.coplan@arm.com>
Date:   Wed Feb 8 13:09:45 2023 +0000

    c: Fixes for build_conditional_expr and __capability propagation
    
    The following hybrid-capability C testcase reveals problems with
    c-typeck.c:build_conditional_expr and capability propagation:
    
    typedef int T;
    struct S1 { int a; };
    struct S2 { T a; };
    int * __capability f(int x,
                         struct S1 * __capability p,
                         struct S2 * __capability q)
    {
        return x ? &p->a : &q->a;
    }
    
    When built with -march=morello, as it stands, we incorrectly compute the
    type of the conditional expression as a non-capability pointer to int
    (i.e. `int *`). The result type should of course be `int * __capability`.
    
    In the case where the two arms of the conditional expression are both of
    POINTER_TYPE, build_conditional_expr first checks if the target types
    are compatible, and if so, invokes common_pointer_type to compute the
    result type.
    
    common_pointer_type already takes care to merge any type attributes on
    the pointer types, so if either type has the "cheri capability"
    attribute, then the result type should also gain this attribute (and
    therefore be a capability type). However, it turns out that the type
    given to &p->a above, while satisfying capability_type_p due to its
    TYPE_MODE being a capability mode, doesn't have the "cheri capability"
    attribute. In general, we don't currently attempt to maintain the
    invariant that all capability pointers in hybrid Morello have the "cheri
    capability" attribute. For example, addr_expr_type currently builds
    capability pointers without the attribute.
    
    In addition to this inconsistency around the "cheri capability"
    attribute, there are other problems in build_conditional_expr itself.
    In the cases where one of the pointer target types is void, and the case
    where the target types are incompatible, build_conditional_expr
    currently uses build_pointer_type to obtain the result type, but this
    will unconditionally give a non-capability pointer on hybrid Morello.
    
    In general, it seems there are two possible approaches to fixing these
    issues:
    
     1. Use build_pointer_type_for_mode instead of build_pointer_type and
        calculate the correct mode manually at each such call site.
     2. Establish the invariant that all hybrid capability pointers have the
        "cheri capability" attribute and rely on type attribute merging to
        propagate capability-ness.
    
    In this patch we prefer the second approach for a number of reasons.
    Among these:
    
     - Code throughout the compiler already makes a reasonable effort to
       preserve/merge type attributes, so this approach means that we're
       more likely to just get the right answer without requiring changes
       across the codebase.
     - In hybrid CHERI, it's really the presence of the __capability
       annotation that gives rise to a pointer with a capability mode.
       Having the mode imply capability-ness seems like the implication is
       the wrong way round.
     - The first approach would require considerable changes throughout the
       codebase, and would require extra code with additional error-prone
       mode calculations at each call site.
    
    In this patch, we add a test that checks the FE output for several cases
    involving conditional expressions and hybrid pointers. This revealed
    that we were missing a warning for the case where the operands have
    mixed capability-ness and one is implicitly DDC-converted.
    
    To assist in establishing the invariant that all hybrid pointers have
    the capability attribute, we add a checking assertion in
    capability_type_p to this effect. Note that we add the seemingly-redundant
    condition of targetm.capability_mode ().exists () here. This is to avoid
    imposing the requirement on the selftests, which currently create
    capability types even when the target is not configured as a capability
    target. This is desirable to ensure that the capability-relevant
    selftests are run even with default/base target options.
    
    We update the cases in build_conditional_expr that previously called
    build_pointer_type to go through change_pointer_target_type and
    common_pointer_type instead. This naturally ensures that any capability
    attributes are preserved.
    
    Since the "cheri capability" attribute is now effectively part of the
    GIMPLE type system (as opposed to just being a c-family attribute), we
    need it to be available during LTO. We update lto-lang.c accordingly.
    
    As follow-on work, it would be good to audit uses of build_pointer_type
    throughout the compiler to make sure that we don't have similar problems
    elsewhere, but this patch should be an incremental improvement on the
    current situation.

Diff:
---
 gcc/c-family/c-pretty-print.c                      |   8 ++
 gcc/c/c-decl.c                                     |   3 +-
 gcc/c/c-typeck.c                                   |  31 +++--
 gcc/lto/lto-lang.c                                 |  25 ++++
 .../gcc.target/aarch64/morello/cond-expr-1.c       |   2 +
 .../aarch64/morello/hybrid-cap-ptr-conversions.c   |   1 +
 .../gcc.target/aarch64/morello/hybrid-cond-expr.c  | 128 +++++++++++++++++++++
 .../gcc.target/aarch64/morello/merge-decls.c       |   9 ++
 gcc/tree.c                                         |  63 ++++++++--
 9 files changed, 250 insertions(+), 20 deletions(-)

diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c
index c225e1e3be7..e59619e2187 100644
--- a/gcc/c-family/c-pretty-print.c
+++ b/gcc/c-family/c-pretty-print.c
@@ -463,6 +463,14 @@ pp_c_specifier_qualifier_list (c_pretty_printer *pp, tree t)
 	else if (!c_dialect_cxx ())
 	  pp_c_whitespace (pp);
 	pp_ptr_operator (pp, t);
+
+	/* Print __capability annotations only if pointers aren't
+	   capabilities by default.  */
+	if (capability_type_p (t) && !capability_type_p (ptr_type_node))
+	  {
+	    pp_c_whitespace (pp);
+	    pp_string (pp, "__capability");
+	  }
       }
       break;
 
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 156914a9cd1..3ce9699e3f8 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -6365,7 +6365,8 @@ grokdeclarator (const struct c_declarator *declarator,
 		{
 		  tree attr_name = get_identifier ("cheri capability");
 		  tree new_attrs = build_tree_list (attr_name, NULL_TREE);
-		  gcc_assert (!decl_attributes (&type, new_attrs, 0));
+		  if (decl_attributes (&type, new_attrs, 0))
+		    gcc_unreachable ();
 		}
 	      deprecated_capability_uses = 1;
 
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index a7181d76a41..998d1c469d6 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -432,7 +432,7 @@ composite_type (tree t1, tree t2)
 	tree pointed_to_1 = TREE_TYPE (t1);
 	tree pointed_to_2 = TREE_TYPE (t2);
 	tree target = composite_type (pointed_to_1, pointed_to_2);
-        t1 = build_pointer_type_for_mode (target, TYPE_MODE (t1), false);
+	t1 = change_pointer_target_type (t1, target);
 	t1 = build_type_attribute_variant (t1, attributes);
 	return qualify_type (t1, t2);
       }
@@ -5462,8 +5462,10 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 	    pedwarn (colon_loc, OPT_Wpedantic,
 		     "ISO C forbids conditional expr between "
 		     "%<void *%> and function pointer");
-	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type1),
-							  TREE_TYPE (type2)));
+
+	  result_type = qualify_type (TREE_TYPE (type1), TREE_TYPE (type2));
+	  result_type = change_pointer_target_type (type2, result_type);
+	  result_type = common_pointer_type (type1, result_type);
 	}
       else if (VOID_TYPE_P (TREE_TYPE (type2))
 	       && !TYPE_ATOMIC (TREE_TYPE (type2)))
@@ -5479,8 +5481,10 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 	    pedwarn (colon_loc, OPT_Wpedantic,
 		     "ISO C forbids conditional expr between "
 		     "%<void *%> and function pointer");
-	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type2),
-							  TREE_TYPE (type1)));
+
+	  result_type = qualify_type (TREE_TYPE (type2), TREE_TYPE (type1));
+	  result_type = change_pointer_target_type (type1, result_type);
+	  result_type = common_pointer_type (result_type, type2);
 	}
       /* Objective-C pointer comparisons are a bit more lenient.  */
       else if (objc_have_common_type (type1, type2, -3, NULL_TREE))
@@ -5496,8 +5500,21 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 	  else
 	    pedwarn (colon_loc, 0,
 		     "pointer type mismatch in conditional expression");
-	  result_type = build_pointer_type
-			  (build_qualified_type (void_type_node, qual));
+
+	  tree tgt = build_qualified_type (void_type_node, qual);
+	  tree t1 = change_pointer_target_type (type1, tgt);
+	  tree t2 = change_pointer_target_type (type2, tgt);
+	  result_type = common_pointer_type (t1, t2);
+	}
+
+      if (capability_type_p (type1) != capability_type_p (type2))
+	{
+	  gcc_assert (capability_type_p (result_type));
+	  tree noncap_type = capability_type_p (type1) ? type2 : type1;
+	  location_t noncap_loc = (noncap_type == type2) ? op2_loc : op1_loc;
+	  warning_at (noncap_loc, OPT_Wcheri_implicit_pointer_conversion_to_cap,
+		      "converting non-capability type %qT to capability type "
+		      "%qT without an explicit cast", noncap_type, result_type);
 	}
     }
   else if (code1 == POINTER_TYPE
diff --git a/gcc/lto/lto-lang.c b/gcc/lto/lto-lang.c
index f0bb351010e..4e1156f086c 100644
--- a/gcc/lto/lto-lang.c
+++ b/gcc/lto/lto-lang.c
@@ -59,6 +59,7 @@ static tree ignore_attribute (tree *, tree, tree, int, bool *);
 
 static tree handle_format_attribute (tree *, tree, tree, int, bool *);
 static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
+static tree handle_cheri_capability_attribute (tree *, tree, tree, int, bool *);
 static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
@@ -130,6 +131,8 @@ const struct attribute_spec lto_attribute_table[] =
 			      handle_type_generic_attribute, NULL },
   { "fn spec",	 	      1, 1, false, true, true, false,
 			      handle_fnspec_attribute, NULL },
+  { "cheri capability",	      0, 0, false, true, false, true,
+			      handle_cheri_capability_attribute, NULL, },
   { "transaction_pure",	      0, 0, false, true, true, false,
 			      handle_transaction_pure_attribute, NULL },
   /* For internal use only.  The leading '*' both prevents its usage in
@@ -583,6 +586,28 @@ handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
   return NULL_TREE;
 }
 
+/* Apply the "cheri capability" attribute; follows the function of the same
+   name from c-family/c-attribs.c.  */
+
+static tree
+handle_cheri_capability_attribute (tree *node, tree, tree, int,
+				   bool *no_add_attrs)
+{
+  *no_add_attrs = true;
+
+  gcc_assert (targetm.capability_mode ().exists ()
+	      && TREE_CODE (*node) == POINTER_TYPE);
+
+  if (!capability_type_p (*node))
+    {
+      tree attrs = tree_cons (get_identifier ("cheri capability"),
+			      NULL_TREE, TYPE_ATTRIBUTES (*node));
+      *node = build_type_attribute_variant (*node, attrs);
+    }
+
+  return NULL_TREE;
+}
+
 /* Cribbed from c-common.c.  */
 
 static void
diff --git a/gcc/testsuite/gcc.target/aarch64/morello/cond-expr-1.c b/gcc/testsuite/gcc.target/aarch64/morello/cond-expr-1.c
index c4ba9cefbdc..2a8d32ad609 100644
--- a/gcc/testsuite/gcc.target/aarch64/morello/cond-expr-1.c
+++ b/gcc/testsuite/gcc.target/aarch64/morello/cond-expr-1.c
@@ -4,5 +4,7 @@
 void foo4(char *__capability p, char *q)
 {
   (p < q ? p : q)();		/* { dg-error "is not a function" } */
+  /* { dg-warning {converting non-capability type 'char \*' to capability type 'char \* __capability'} "" { target cheri_capability_hybrid } .-1 } */
   (p > q ? p : q)();		/* { dg-error "is not a function" } */
+  /* { dg-warning {converting non-capability type 'char \*' to capability type 'char \* __capability'} "" { target cheri_capability_hybrid } .-1 } */
 }
diff --git a/gcc/testsuite/gcc.target/aarch64/morello/hybrid-cap-ptr-conversions.c b/gcc/testsuite/gcc.target/aarch64/morello/hybrid-cap-ptr-conversions.c
index 6a8a4faef4f..4af288fd80e 100644
--- a/gcc/testsuite/gcc.target/aarch64/morello/hybrid-cap-ptr-conversions.c
+++ b/gcc/testsuite/gcc.target/aarch64/morello/hybrid-cap-ptr-conversions.c
@@ -79,6 +79,7 @@ void bar2 ()
 int *cond1 (int *a, int * __capability b)
 {
   return a ? b : a;  /* { dg-warning {converting capability pointer to non-capability pointer without an explicit cast} } */
+  /* { dg-warning {converting non-capability type 'int \*' to capability type 'int \* __capability' without an explicit cast} "" { target *-*-* } .-1 } */
 }
 // TO capability
 int * __capability cond2 (int *a, int *  b)
diff --git a/gcc/testsuite/gcc.target/aarch64/morello/hybrid-cond-expr.c b/gcc/testsuite/gcc.target/aarch64/morello/hybrid-cond-expr.c
new file mode 100644
index 00000000000..4b7b0ad1161
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/morello/hybrid-cond-expr.c
@@ -0,0 +1,128 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target cheri_capability_hybrid } */
+/* { dg-additional-options "-fdump-tree-original" } */
+
+typedef int T;
+struct S1 { int a; };
+struct S2 { T a; };
+struct S3 { char a; };
+
+int * __capability f(int x1, struct S1 * __capability p1, struct S2 * __capability q1)
+{
+    return x1 ? &p1->a : &q1->a;
+}
+/* { dg-final { scan-tree-dump-times {return x1 != 0 \? &p1->a : \(int \* __capability\) &q1->a;} 1 "original" } } */
+
+int * __capability warn1(int x2, struct S1 * __capability p2, struct S2 * q2)
+{
+  return x2 ? &p2->a : &q2->a; /* { dg-warning {converting non-capability type 'T \*' {aka 'int \*'} to capability type 'int \* __capability' without an explicit cast} } */
+}
+/* { dg-final { scan-tree-dump-times {return x2 != 0 \? &p2->a : \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) &q2->a\);} 1 "original" } } */
+
+int * __capability warn2(int x3, struct S1 *p3, struct S2 * __capability q3)
+{
+  return x3 ? &p3->a : &q3->a; /* { dg-warning {converting non-capability type 'int \*' to capability type 'int \* __capability' without an explicit cast} } */
+}
+/* { dg-final { scan-tree-dump-times {return x3 != 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) &p3->a\) : \(int \* __capability\) } 1 "original" } } */
+
+void * __capability mismatch1(int x4, int * __capability p4, char * __capability q4)
+{
+  return x4 ? p4 : q4; /* { dg-warning {pointer type mismatch in conditional expression} } */
+}
+/* { dg-final { scan-tree-dump-times {return x4 != 0 \? \(void \* __capability\) p4 : \(void \* __capability\) q4;} 1 "original" } } */
+
+void * __capability mismatch2(int x5, int * __capability p5, char *q5)
+{
+  return x5 ? p5 : q5 ; /* { dg-warning {pointer type mismatch in conditional expression} } */
+  /* { dg-warning {converting non-capability type 'char \*' to capability type * 'void \* __capability' without an explicit cast} "" { target *-*-*} .-1 } */
+}
+/* { dg-final { scan-tree-dump-times {return x5 == 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) q5\) : \(void \* __capability\) p5;} 1 "original" } } */
+
+void * __capability mismatch3(int x6, int *p6, char * __capability q6)
+{
+  return x6 ? p6 : q6; /* { dg-warning {pointer type mismatch in conditional expression} } */
+  /* { dg-warning {converting non-capability type 'int \*' to capability type * 'void \* __capability' without an explicit cast} "" { target *-*-*} .-1 } */
+}
+/* { dg-final { scan-tree-dump-times {return x6 != 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) p6\) : \(void \* __capability\) q6;} 1 "original" } } */
+
+void * __capability mismatch4(int x7, struct S1 * __capability p7, struct S3 * __capability q7)
+{
+  return x7 ? &p7->a : &q7->a; /* { dg-warning {pointer type mismatch in conditional expression} } */
+}
+/* { dg-final { scan-tree-dump-times {return x7 != 0 \? \(void \* __capability\) &p7->a : \(void \* __capability\) &q7->a;} 1 "original" } } */
+
+void * __capability mismatch5(int x8, struct S1 * __capability p8, struct S3 *q8)
+{
+  return x8 ? &p8->a : &q8->a; /* { dg-warning {pointer type mismatch in conditional expression} } */
+  /* { dg-warning {converting non-capability type 'char \*' to capability type * 'void \* __capability' without an explicit cast} "" { target *-*-*} .-1 } */
+}
+/* { dg-final { scan-tree-dump-times {return x8 != 0 \? \(void \* __capability\) &p8->a : \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) &q8->a\);} 1 "original" } } */
+
+void * __capability mismatch6(int x9, struct S1 *p9, struct S3 * __capability q9)
+{
+  return x9 ? &p9->a : &q9->a; /* { dg-warning {pointer type mismatch in conditional expression} } */
+  /* { dg-warning {converting non-capability type 'int \*' to capability type * 'void \* __capability' without an explicit cast} "" { target *-*-*} .-1 } */
+}
+/* { dg-final { scan-tree-dump-times {return x9 != 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) &p9->a\) : \(void \* __capability\) &q9->a;} 1 "original" } } */
+
+void * __capability mismatch7(int x10, struct S2 * __capability p10, struct S3 * __capability q10)
+{
+  return x10 ? &p10->a : &q10->a; /* { dg-warning {pointer type mismatch in conditional expression} } */
+}
+/* { dg-final { scan-tree-dump-times {return x10 != 0 \? \(void \* __capability\) &p10->a : \(void \* __capability\) &q10->a;} 1 "original" } } */
+
+void * __capability mismatch8(int x11, struct S2 * __capability p11, struct S3 *q11)
+{
+  return x11 ? &p11->a : &q11->a; /* { dg-warning {pointer type mismatch in conditional expression} } */
+  /* { dg-warning {converting non-capability type 'char \*' to capability type * 'void \* __capability' without an explicit cast} "" { target *-*-*} .-1 } */
+}
+/* { dg-final { scan-tree-dump-times {return x11 != 0 \? \(void \* __capability\) &p11->a : \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) &q11->a\);} 1 "original" } } */
+
+void * __capability mismatch9(int x12, struct S2 *p12, struct S3 * __capability q12)
+{
+  return x12 ? &p12->a : &q12->a; /* { dg-warning {pointer type mismatch in conditional expression} } */
+  /* { dg-warning {converting non-capability type 'T \*' {aka 'int \*'} to capability type 'void \* __capability' without an explicit cast} "" { target *-*-* } .-1 } */
+}
+/* { dg-final { scan-tree-dump-times {return x12 != 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) &p12->a\) : \(void \* __capability\) &q12->a;} 1 "original" } } */
+
+void * __capability void1 (int x13, void * __capability p13, int * __capability q13)
+{
+  return x13 ? p13 : q13;
+}
+/* { dg-final { scan-tree-dump-times {return x13 != 0 \? p13 : \(void \* __capability\) q13;} 1 "original" } } */
+
+void * __capability void2 (int x14, int * __capability p14, void * __capability q14)
+{
+  return x14 ? p14 : q14;
+}
+/* { dg-final { scan-tree-dump-times {return x14 != 0 \? \(void \* __capability\) p14 : q14;} 1 "original" } } */
+
+void * __capability void3 (int x15, void * __capability p15, int *q15)
+{
+  return x15 ? p15 : q15; /* { dg-warning {converting non-capability type 'int \*' to capability type 'void \* __capability' without an explicit cast} } */
+}
+/* { dg-final { scan-tree-dump-times {return x15 == 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) q15\) : p15;} 1 "original" } } */
+
+void * __capability void4 (int x16, void *p16, int * __capability q16)
+{
+  return x16 ? p16 : q16; /* { dg-warning {converting non-capability type 'void \*' to capability type 'void \* __capability' without an explicit cast} } */
+}
+/* { dg-final { scan-tree-dump-times {return x16 != 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) p16\) : \(void \* __capability\) q16;} 1 "original" } } */
+
+void * __capability void5 (int x17, int * __capability p17, void *q17)
+{
+  return x17 ? p17 : q17; /* { dg-warning {converting non-capability type 'void \*' to capability type 'void \* __capability' without an explicit cast} } */
+}
+/* { dg-final { scan-tree-dump-times {return x17 == 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) q17\) : \(void \* __capability\) p17;} 1 "original" } } */
+
+void * __capability void6 (int x18, int *p18, void * __capability q18)
+{
+  return x18 ? p18 : q18; /* { dg-warning {converting non-capability type 'int \*' to capability type 'void \* __capability' without an explicit cast} } */
+}
+/* { dg-final { scan-tree-dump-times {return x18 != 0 \? \.REPLACE_ADDRESS_VALUE \(\.CAP_GLOBAL_DATA_GET \(\), \(long unsigned int\) p18\) : q18;} 1 "original" } } */
+
+void * __capability void7 (int x19, void * __capability p19, struct S2 * __capability q19)
+{
+  return x19 ? p19 : &q19->a;
+}
+/* { dg-final { scan-tree-dump-times {return x19 == 0 \? \(void \* __capability\) &q19->a : p19;} 1 "original" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/morello/merge-decls.c b/gcc/testsuite/gcc.target/aarch64/morello/merge-decls.c
new file mode 100644
index 00000000000..06ea7de4eed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/morello/merge-decls.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* This testcase hits c-typeck.c:composite_type with capability pointer type operands.
+
+   This revealed a problem in composite_type leading to ICEs when
+   compiling for hybrid Morello, but this wasn't hit elsewhere by the
+   testsuite.  */
+typedef int T;
+void f(T * __capability);
+void f(int * __capability);
diff --git a/gcc/tree.c b/gcc/tree.c
index 9b778220490..33399f0bd88 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -8108,10 +8108,19 @@ tree
 addr_expr_type (tree_code code, tree op_type)
 {
   gcc_assert (ADDR_EXPR_CODE_P (code) && TYPE_P (op_type));
-  auto as = TYPE_ADDR_SPACE (op_type);
-  bool is_capability = (code == CAP_ADDR_EXPR);
-  auto ptr_mode = targetm.addr_space.pointer_mode (as, is_capability);
-  return build_pointer_type_for_mode (op_type, ptr_mode, false);
+  tree t = build_pointer_type (op_type);
+
+  /* If pointers aren't capabilities by default, and we want a
+     capability type, apply the "cheri capability" attribute.  */
+  if (code == CAP_ADDR_EXPR && !capability_type_p (t))
+    {
+      tree attrs = build_tree_list (get_identifier ("cheri capability"),
+				    NULL_TREE);
+      if (decl_attributes (&t, attrs, 0))
+	gcc_unreachable ();
+    }
+
+  return t;
 }
 
 /* Try building a capability pointer to TO_TYPE.  Return error_mark_node
@@ -10620,11 +10629,12 @@ build_common_tree_nodes (bool signed_char)
 		  == intcap_type_node);
       gcc_assert (build_intcap_type_for_mode (cap_mode, 1)
 		  == uintcap_type_node);
-      cap_ptr_type_node = build_pointer_type_for_mode (void_type_node,
-						       cap_mode, false);
+      cap_ptr_type_node
+	= try_building_capability_pointer_type (void_type_node);
       cap_const_ptr_type_node
-	= build_pointer_type_for_mode
-	      (build_type_variant (void_type_node, 1, 0), cap_mode, false);
+	= change_pointer_target_type (cap_ptr_type_node,
+				      build_type_variant (void_type_node,
+							  1, 0));
     }
 
   float_type_node = make_node (REAL_TYPE);
@@ -14151,6 +14161,18 @@ capability_type_p (const_tree t)
   /* We should only be seeing pointers as capabilities at the moment.  */
   gcc_assert (POINTER_TYPE_P (t) || TREE_CODE (t) == NULLPTR_TYPE
 	      || INTCAP_TYPE_P (t) || AGGREGATE_TYPE_P (t));
+
+  if (CHECKING_P
+      && targetm.capability_mode ().exists ()
+      && POINTER_TYPE_P (t)
+      && t != ptr_type_node
+      && !capability_type_p (ptr_type_node))
+    {
+      /* For hybrid, we want the invariant that all capability pointers
+	 have the "cheri capability" attribute.  */
+      gcc_assert (lookup_attribute ("cheri capability", TYPE_ATTRIBUTES (t)));
+    }
+
   return (POINTER_TYPE_P (t) || TREE_CODE (t) == NULLPTR_TYPE
 	  || INTCAP_TYPE_P (t));
 }
@@ -16390,6 +16412,23 @@ test_escaped_strings (void)
   pp_line_cutoff (global_dc->printer) = saved_cutoff;
 }
 
+static tree
+force_build_capability_pointer_type (tree target)
+{
+  if (!targetm.capability_mode ().exists ())
+    {
+      /* Typically the selftests will be run without any target options, so
+	 we'll usually end up in this branch.  We still want to run the
+	 capability-relevant selftests in this case, though, so here we just
+	 force building a pointer with underlying capability mode.  */
+      return build_pointer_type_for_mode (target, CADImode, false);
+    }
+
+  /* Otherwise, we're running the selftests with target options that make us a
+     real capability target.  Build the capability type properly.  */
+  return try_building_capability_pointer_type (target);
+}
+
 static void
 test_fold_drop_capability (void)
 {
@@ -16429,7 +16468,7 @@ test_fold_drop_capability (void)
 	tcc_statement ??
 	*/
   tree cap_pointer_type
-    = build_pointer_type_for_mode (integer_type_node, CADImode, false);
+    = force_build_capability_pointer_type (integer_type_node);
   tree cap_pointer_cst = build_int_cst (cap_pointer_type, 0);
   tree pointer_offset_type = noncapability_type (TREE_TYPE (cap_pointer_cst));
   tree pointer_offset_type2 = noncapability_type (cap_pointer_type);
@@ -16475,7 +16514,7 @@ static void
 test_capability_type_manipulation (void)
 {
   tree cap_pointer_type
-    = build_pointer_type_for_mode (integer_type_node, CADImode, false);
+    = force_build_capability_pointer_type (integer_type_node);
   ASSERT_EQ (true, capability_type_p (cap_pointer_type));
   ASSERT_EQ (false, capability_type_p (integer_type_node));
   ASSERT_EQ (false, capability_type_p (NULL_TREE));
@@ -16494,7 +16533,7 @@ static void
 test_fold_build_replace_address_value (void)
 {
   tree cap_pointer_type
-    = build_pointer_type_for_mode (integer_type_node, CADImode, false);
+    = force_build_capability_pointer_type (integer_type_node);
   /* BASE_TEST is designed to test only values (and not metadata bits).  */
 #define BASE_TEST(CV, NEW) \
   { \
@@ -16522,7 +16561,7 @@ test_fold_build_replace_address_value (void)
 static void
 test_maybe_cap_all_onesp (void)
 {
-  tree ty = build_pointer_type_for_mode (integer_type_node, CADImode, false);
+  tree ty = force_build_capability_pointer_type (integer_type_node);
 
   ASSERT_FALSE (maybe_cap_all_onesp (build_int_cst (ty, 0)));
   ASSERT_FALSE (maybe_cap_all_onesp (build_int_cst (ty, 42)));

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-02-28 10:09 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-28 10:09 [gcc(refs/vendors/ARM/heads/morello)] c: Fixes for build_conditional_expr and __capability propagation Alex Coplan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).