public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/vendors/ARM/heads/morello)] Handle overloading of __builtin_cheri functions
@ 2022-06-16 13:45 Richard Sandiford
  0 siblings, 0 replies; only message in thread
From: Richard Sandiford @ 2022-06-16 13:45 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:f3f286d02a5c8041f250145aa2223150f6b08fcb

commit f3f286d02a5c8041f250145aa2223150f6b08fcb
Author: Richard Sandiford <richard.sandiford@arm.com>
Date:   Thu Jun 16 14:42:39 2022 +0100

    Handle overloading of __builtin_cheri functions
    
    This patch adds overloading of __builtin_cheri functions, using the
    approach described above resolve_cheri_builtin.  Initially I'd tried
    some other approaches, the first of which meant having to move the
    built-in definitions from aarch64 to generic code.  That probably
    wasn't necessary with the final version, but given that I'd done
    all the work before switching to that, it seemed worth keeping
    anyway.
    
    (The earlier versions tried to make the whole IL polymorphic,
    to reduce the amount of casting, but that got quite ugly.)
    
    Testing this for C++ on hybrid showed a couple of places where
    we weren't preserving the “capability-ness” of pointers.
    
    I'll follow up with a patch to move the remaining non-overloaded
    functions to target-independent code too (just for consistency,
    not because it's inherently better).

Diff:
---
 gcc/builtin-types.def                          |  15 +
 gcc/builtins.c                                 |  91 +++++-
 gcc/builtins.def                               |  57 ++++
 gcc/builtins.h                                 |   1 +
 gcc/c-family/c-common.c                        |  97 ++++++
 gcc/config/aarch64/aarch64-builtins.c          | 404 -------------------------
 gcc/config/aarch64/aarch64-morello.md          |  52 ++--
 gcc/cp/call.c                                  |   2 +-
 gcc/cp/tree.c                                  |   2 +-
 gcc/doc/md.texi                                | 215 +++++++++++++
 gcc/gimplify.c                                 |  47 ++-
 gcc/internal-fn.c                              |  17 ++
 gcc/internal-fn.def                            |  51 ++++
 gcc/internal-fn.h                              |   1 +
 gcc/optabs.def                                 |  26 ++
 gcc/testsuite/c-c++-common/cheri-overloads-1.c | 224 ++++++++++++++
 gcc/testsuite/c-c++-common/cheri-overloads-2.c |  80 +++++
 gcc/testsuite/c-c++-common/cheri-overloads-3.c | 185 +++++++++++
 gcc/testsuite/c-c++-common/cheri-overloads-4.c | 217 +++++++++++++
 19 files changed, 1348 insertions(+), 436 deletions(-)

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index e07bc092769..f54d7703a73 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -74,6 +74,9 @@ DEF_PRIMITIVE_TYPE (BT_ICAPOFF, intcap_type_node
 				? TREE_TYPE (intcap_type_node)
 				: error_mark_node)
 DEF_PRIMITIVE_TYPE (BT_UINT, unsigned_type_node)
+DEF_PRIMITIVE_TYPE (BT_UICAP, uintcap_type_node
+			      ? uintcap_type_node
+			      : error_mark_node)
 DEF_PRIMITIVE_TYPE (BT_LONG, long_integer_type_node)
 DEF_PRIMITIVE_TYPE (BT_ULONG, long_unsigned_type_node)
 DEF_PRIMITIVE_TYPE (BT_LONGLONG, long_long_integer_type_node)
@@ -89,6 +92,7 @@ DEF_PRIMITIVE_TYPE (BT_UINT64, uint64_type_node)
 DEF_PRIMITIVE_TYPE (BT_UINT128, uint128_type_node
 				? uint128_type_node
 				: error_mark_node)
+DEF_PRIMITIVE_TYPE (BT_PTRDIFF, ptrdiff_type_node)
 DEF_PRIMITIVE_TYPE (BT_WORD, (*lang_hooks.types.type_for_mode) (word_mode, 1))
 DEF_PRIMITIVE_TYPE (BT_UNWINDWORD, (*lang_hooks.types.type_for_mode)
 				    (targetm.unwind_word_mode (), 1))
@@ -282,8 +286,10 @@ DEF_FUNCTION_TYPE_1 (BT_FN_LONGLONG_DOUBLE, BT_LONGLONG, BT_DOUBLE)
 DEF_FUNCTION_TYPE_1 (BT_FN_LONGLONG_LONGDOUBLE, BT_LONGLONG, BT_LONGDOUBLE)
 DEF_FUNCTION_TYPE_1 (BT_FN_VOID_PTR, BT_VOID, BT_PTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_SIZE_CONST_STRING, BT_SIZE, BT_CONST_STRING)
+DEF_FUNCTION_TYPE_1 (BT_FN_SIZE_CAPPTR, BT_SIZE, BT_CAPPTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_INT_CONST_STRING, BT_INT, BT_CONST_STRING)
 DEF_FUNCTION_TYPE_1 (BT_FN_PTR_PTR, BT_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_1 (BT_FN_CAPPTR_CAPPTR, BT_CAPPTR, BT_CAPPTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_PTRMODE_PTR, BT_PTRMODE, BT_PTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_VOID_VALIST_REF, BT_VOID, BT_VALIST_REF)
 DEF_FUNCTION_TYPE_1 (BT_FN_VOID_INT, BT_VOID, BT_INT)
@@ -336,12 +342,14 @@ DEF_FUNCTION_TYPE_1 (BT_FN_UINT128_UINT128, BT_UINT128, BT_UINT128)
 DEF_FUNCTION_TYPE_1 (BT_FN_UINT64_FLOAT, BT_UINT64, BT_FLOAT)
 DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_INT, BT_BOOL, BT_INT)
 DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_PTR, BT_BOOL, BT_PTR)
+DEF_FUNCTION_TYPE_1 (BT_FN_BOOL_CAPPTR, BT_BOOL, BT_CAPPTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_PTR_CONST_PTR, BT_PTR, BT_CONST_PTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_CONST_PTR_CONST_PTR, BT_CONST_PTR, BT_CONST_PTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_UINT16_UINT32, BT_UINT16, BT_UINT32)
 DEF_FUNCTION_TYPE_1 (BT_FN_UINT32_UINT16, BT_UINT32, BT_UINT16)
 DEF_FUNCTION_TYPE_1 (BT_FN_INT_FENV_T_PTR, BT_INT, BT_FENV_T_PTR)
 DEF_FUNCTION_TYPE_1 (BT_FN_INT_CONST_FENV_T_PTR, BT_INT, BT_CONST_FENV_T_PTR)
+DEF_FUNCTION_TYPE_1 (BT_FN_PTRDIFF_CAPPTR, BT_PTRDIFF, BT_CAPPTR)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR)
 
@@ -510,6 +518,10 @@ DEF_FUNCTION_TYPE_2 (BT_FN_PTR_SIZE_SIZE,
 		     BT_PTR, BT_SIZE, BT_SIZE)
 DEF_FUNCTION_TYPE_2 (BT_FN_PTR_PTR_SIZE,
 		     BT_PTR, BT_PTR, BT_SIZE)
+DEF_FUNCTION_TYPE_2 (BT_FN_CAPPTR_CAPPTR_SIZE,
+		     BT_CAPPTR, BT_CAPPTR, BT_SIZE)
+DEF_FUNCTION_TYPE_2 (BT_FN_CAPPTR_CAPPTR_UICAP,
+		     BT_CAPPTR, BT_CAPPTR, BT_UICAP)
 DEF_FUNCTION_TYPE_2 (BT_FN_COMPLEX_FLOAT_COMPLEX_FLOAT_COMPLEX_FLOAT,
 		     BT_COMPLEX_FLOAT, BT_COMPLEX_FLOAT, BT_COMPLEX_FLOAT)
 DEF_FUNCTION_TYPE_2 (BT_FN_COMPLEX_DOUBLE_COMPLEX_DOUBLE_COMPLEX_DOUBLE,
@@ -524,6 +536,7 @@ DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_LONGPTR_LONGPTR,
 		     BT_BOOL, BT_PTR_LONG, BT_PTR_LONG)
 DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_ULONGLONGPTR_ULONGLONGPTR,
 		     BT_BOOL, BT_PTR_ULONGLONG, BT_PTR_ULONGLONG)
+DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_CAPPTR_CAPPTR, BT_BOOL, BT_CAPPTR, BT_CAPPTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_VOID_VPTR_INT, BT_VOID, BT_VOLATILE_PTR, BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_VPTR_INT, BT_BOOL, BT_VOLATILE_PTR, BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_BOOL_SIZE_CONST_VPTR, BT_BOOL, BT_SIZE,
@@ -543,6 +556,8 @@ DEF_FUNCTION_TYPE_2 (BT_FN_INT_FEXCEPT_T_PTR_INT, BT_INT, BT_FEXCEPT_T_PTR,
 		     BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, BT_INT,
 		     BT_CONST_FEXCEPT_T_PTR, BT_INT)
+DEF_FUNCTION_TYPE_2 (BT_FN_CAPPTR_CAPPTR_CAPPTR,
+		     BT_CAPPTR, BT_CAPPTR, BT_CAPPTR)
 
 DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_PTR, BT_FN_VOID_PTR_PTR)
 
diff --git a/gcc/builtins.c b/gcc/builtins.c
index a52982799c2..2df8ca739ff 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -2190,6 +2190,95 @@ mathfn_built_in (tree type, enum built_in_function fn)
   return mathfn_built_in_1 (type, as_combined_fn (fn), /*implicit=*/ 1);
 }
 
+/* If CODE is a __builtin_cheri_* function that corresponds to a
+   directly-mapped internal function, return that internal function,
+   otherwise return IFN_LAST.  */
+
+internal_fn
+direct_cheri_internal_fn (built_in_function code)
+{
+  switch (code)
+    {
+    case BUILT_IN_CHERI_ADDRESS_GET:
+      return IFN_CAP_ADDRESS_GET;
+
+    case BUILT_IN_CHERI_BASE_GET:
+      return IFN_CAP_BASE_GET;
+
+    case BUILT_IN_CHERI_BOUNDS_SET:
+      return IFN_CAP_BOUNDS_SET;
+
+    case BUILT_IN_CHERI_BOUNDS_SET_EXACT:
+      return IFN_CAP_BOUNDS_SET_EXACT;
+
+    case BUILT_IN_CHERI_CAP_BUILD:
+      return IFN_CAP_BUILD;
+
+    case BUILT_IN_CHERI_CAP_TYPE_COPY:
+      return IFN_CAP_TYPE_COPY;
+
+    case BUILT_IN_CHERI_CONDITIONAL_SEAL:
+      return IFN_CAP_CONDITIONAL_SEAL;
+
+    case BUILT_IN_CHERI_COPY_FROM_HIGH:
+      return IFN_CAP_COPY_FROM_HIGH;
+
+    case BUILT_IN_CHERI_COPY_TO_HIGH:
+      return IFN_CAP_COPY_TO_HIGH;
+
+    case BUILT_IN_CHERI_EQUAL_EXACT:
+      return IFN_CAP_EQUAL_EXACT;
+
+    case BUILT_IN_CHERI_FLAGS_GET:
+      return IFN_CAP_FLAGS_GET;
+
+    case BUILT_IN_CHERI_FLAGS_SET:
+      return IFN_CAP_FLAGS_SET;
+
+    case BUILT_IN_CHERI_LENGTH_GET:
+      return IFN_CAP_LENGTH_GET;
+
+    case BUILT_IN_CHERI_OFFSET_GET:
+      return IFN_CAP_OFFSET_GET;
+
+    case BUILT_IN_CHERI_OFFSET_SET:
+      return IFN_CAP_OFFSET_SET;
+
+    case BUILT_IN_CHERI_PERMS_AND:
+      return IFN_CAP_PERMS_AND;
+
+    case BUILT_IN_CHERI_PERMS_GET:
+      return IFN_CAP_PERMS_GET;
+
+    case BUILT_IN_CHERI_SEAL:
+      return IFN_CAP_SEAL;
+
+    case BUILT_IN_CHERI_SEAL_ENTRY:
+      return IFN_CAP_SEAL_ENTRY;
+
+    case BUILT_IN_CHERI_SEALED_GET:
+      return IFN_CAP_SEALED_GET;
+
+    case BUILT_IN_CHERI_SUBSET_TEST:
+      return IFN_CAP_SUBSET_TEST;
+
+    case BUILT_IN_CHERI_TAG_CLEAR:
+      return IFN_CAP_TAG_CLEAR;
+
+    case BUILT_IN_CHERI_TAG_GET:
+      return IFN_CAP_TAG_GET;
+
+    case BUILT_IN_CHERI_TYPE_GET:
+      return IFN_CAP_TYPE_GET;
+
+    case BUILT_IN_CHERI_UNSEAL:
+      return IFN_CAP_UNSEAL;
+
+    default:
+      return IFN_LAST;
+    }
+}
+
 /* If BUILT_IN_NORMAL function FNDECL has an associated internal function,
    return its code, otherwise return IFN_LAST.  Note that this function
    only tests whether the function is defined in internals.def, not whether
@@ -2224,7 +2313,7 @@ associated_internal_fn (tree fndecl)
       return IFN_LAST;
 
     default:
-      return IFN_LAST;
+      return direct_cheri_internal_fn (DECL_FUNCTION_CODE (fndecl));
     }
 }
 
diff --git a/gcc/builtins.def b/gcc/builtins.def
index d3f0361bd9e..4020569525c 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -1075,6 +1075,63 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
 /* Coroutine builtins.  */
 #include "coroutine-builtins.def"
 
+/* CHERI builtins.  */
+
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_ADDRESS_GET, "cheri_address_get",
+		 BT_FN_SIZE_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_ADDRESS_SET, "cheri_address_set",
+		 BT_FN_CAPPTR_CAPPTR_SIZE, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_BASE_GET, "cheri_base_get",
+		 BT_FN_SIZE_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_BOUNDS_SET, "cheri_bounds_set",
+		 BT_FN_CAPPTR_CAPPTR_SIZE, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_BOUNDS_SET_EXACT, "cheri_bounds_set_exact",
+		 BT_FN_CAPPTR_CAPPTR_SIZE, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_CAP_BUILD, "cheri_cap_build",
+		 BT_FN_CAPPTR_CAPPTR_UICAP, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_CAP_TYPE_COPY, "cheri_cap_type_copy",
+		 BT_FN_CAPPTR_CAPPTR_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_CONDITIONAL_SEAL, "cheri_conditional_seal",
+		 BT_FN_CAPPTR_CAPPTR_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_COPY_FROM_HIGH, "cheri_copy_from_high",
+		 BT_FN_SIZE_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_COPY_TO_HIGH, "cheri_copy_to_high",
+		 BT_FN_CAPPTR_CAPPTR_SIZE, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_EQUAL_EXACT, "cheri_equal_exact",
+		 BT_FN_BOOL_CAPPTR_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_FLAGS_GET, "cheri_flags_get",
+		 BT_FN_SIZE_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_FLAGS_SET, "cheri_flags_set",
+		 BT_FN_CAPPTR_CAPPTR_SIZE, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_LENGTH_GET, "cheri_length_get",
+		 BT_FN_SIZE_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_OFFSET_GET, "cheri_offset_get",
+		 BT_FN_SIZE_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_OFFSET_INCREMENT, "cheri_offset_increment",
+		 BT_FN_CAPPTR_CAPPTR_SIZE, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_OFFSET_SET, "cheri_offset_set",
+		 BT_FN_CAPPTR_CAPPTR_SIZE, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_PERMS_AND, "cheri_perms_and",
+		 BT_FN_CAPPTR_CAPPTR_SIZE, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_PERMS_GET, "cheri_perms_get",
+		 BT_FN_SIZE_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_SEAL, "cheri_seal",
+		 BT_FN_CAPPTR_CAPPTR_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_SEAL_ENTRY, "cheri_seal_entry",
+		 BT_FN_CAPPTR_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_SEALED_GET, "cheri_sealed_get",
+		 BT_FN_BOOL_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_SUBSET_TEST, "cheri_subset_test",
+		 BT_FN_BOOL_CAPPTR_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_TAG_CLEAR, "cheri_tag_clear",
+		 BT_FN_CAPPTR_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_TAG_GET, "cheri_tag_get",
+		 BT_FN_BOOL_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_TYPE_GET, "cheri_type_get",
+		 BT_FN_PTRDIFF_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+DEF_GCC_BUILTIN (BUILT_IN_CHERI_UNSEAL, "cheri_unseal",
+		 BT_FN_CAPPTR_CAPPTR_CAPPTR, ATTR_CONST_NOTHROW_LIST)
+
 /* Do not expose the BRIG builtins by default gcc-wide, but only privately in
    the BRIG FE as long as there are no references for them in the middle end
    or any of the upstream backends.  */
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 2055549aed1..f1af52f89f2 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -149,6 +149,7 @@ extern char target_percent_c[3];
 extern char target_percent_s_newline[4];
 extern bool target_char_cst_p (tree t, char *p);
 
+extern internal_fn direct_cheri_internal_fn (built_in_function);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 20030960b33..5fc0a75fd44 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -52,6 +52,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "selftest.h"
 #include "debug.h"
 #include "builtins.h"
+#include "internal-fn.h"
 
 cpp_reader *parse_in;		/* Declared in c-pragma.h.  */
 
@@ -7855,6 +7856,73 @@ resolve_atomic_fncode_n (tree function, vec<tree, va_gc> *params,
   return builtin_decl_explicit (new_code_bt);
 }
 
+/* A subroutine of resolve_overloaded_builtin, with the same arguments.
+   FUNCTION is known to be an overloaded BUILT_IN_CHERI_* function
+   with the following properties:
+
+   - Arguments with declared type cap_ptr_type_node can in fact be
+     any capability type, not just "void *__capability".
+
+   - A declared return type of cap_ptr_type_node represents the same type
+     as the first argument of type cap_ptr_type_node.
+
+   We can "resolve" the overload by casting all cap_ptr_type_node arguments
+   to cap_ptr_type_node and casting any cap_ptr_type_node result to its
+   real type (determined by the first cap_ptr_type_node argument).
+   The called function itself doesn't change.
+
+   Doing things this way ensures that full semantic analysis is applied
+   to the adjusted call, such as checking the types of non-overloaded
+   arguments.  */
+
+static tree
+resolve_cheri_builtin (location_t loc, tree function,
+		       vec<tree, va_gc> *params)
+{
+  unsigned int num_params = vec_safe_length (params);
+
+  vec<tree, va_gc> *new_params = vec_safe_copy (params);
+  vec<tree, va_gc> *origtypes = vec_safe_copy (params);
+  for (unsigned int i = 0; i < num_params; ++i)
+    (*origtypes)[i] = ((*origtypes)[i] == error_mark_node
+		       ? error_mark_node
+		       : TREE_TYPE ((*origtypes)[i]));
+
+  tree cap_type = NULL_TREE;
+  tree arg_type;
+  function_args_iterator arg_iter;
+  unsigned int i = 0;
+  FOREACH_FUNCTION_ARGS (TREE_TYPE (function), arg_type, arg_iter)
+    {
+      if (i < num_params && arg_type == cap_ptr_type_node)
+	{
+	  tree param = (*params)[i];
+	  tree param_type = (integer_zerop (param)
+			     ? arg_type
+			     : (*origtypes)[i]);
+	  if (param_type != error_mark_node
+	      && !capability_type_p (param_type))
+	    {
+	      error_at (loc, "passing %qT to argument %u of %qE, "
+			"which expects a capability type",
+			param_type, i + 1, function);
+	      param = build_zero_cst (arg_type);
+	      param_type = error_mark_node;
+	    }
+	  if (!cap_type)
+	    cap_type = param_type;
+	  (*new_params)[i] = convert (arg_type, param);
+	}
+      i += 1;
+    }
+
+  tree res = build_function_call_vec (loc, vNULL, function, new_params,
+				      origtypes, NULL_TREE);
+  if (cap_type && TREE_TYPE (TREE_TYPE (function)) == cap_ptr_type_node)
+    res = convert (cap_type, res);
+  return res;
+}
+
 /* Some builtin functions are placeholders for other expressions.  This
    function should be called immediately after parsing the call expression
    before surrounding code has committed to the type of the expression.
@@ -7944,6 +8012,35 @@ resolve_overloaded_builtin (location_t loc, tree function,
 	  }
       }
 
+    case BUILT_IN_CHERI_ADDRESS_GET:
+    case BUILT_IN_CHERI_ADDRESS_SET:
+    case BUILT_IN_CHERI_BASE_GET:
+    case BUILT_IN_CHERI_BOUNDS_SET:
+    case BUILT_IN_CHERI_BOUNDS_SET_EXACT:
+    case BUILT_IN_CHERI_CAP_BUILD:
+    case BUILT_IN_CHERI_CAP_TYPE_COPY:
+    case BUILT_IN_CHERI_CONDITIONAL_SEAL:
+    case BUILT_IN_CHERI_COPY_FROM_HIGH:
+    case BUILT_IN_CHERI_COPY_TO_HIGH:
+    case BUILT_IN_CHERI_EQUAL_EXACT:
+    case BUILT_IN_CHERI_FLAGS_GET:
+    case BUILT_IN_CHERI_FLAGS_SET:
+    case BUILT_IN_CHERI_LENGTH_GET:
+    case BUILT_IN_CHERI_OFFSET_GET:
+    case BUILT_IN_CHERI_OFFSET_INCREMENT:
+    case BUILT_IN_CHERI_OFFSET_SET:
+    case BUILT_IN_CHERI_PERMS_AND:
+    case BUILT_IN_CHERI_PERMS_GET:
+    case BUILT_IN_CHERI_SEAL:
+    case BUILT_IN_CHERI_SEAL_ENTRY:
+    case BUILT_IN_CHERI_SEALED_GET:
+    case BUILT_IN_CHERI_SUBSET_TEST:
+    case BUILT_IN_CHERI_TAG_CLEAR:
+    case BUILT_IN_CHERI_TAG_GET:
+    case BUILT_IN_CHERI_TYPE_GET:
+    case BUILT_IN_CHERI_UNSEAL:
+      return resolve_cheri_builtin (loc, function, params);
+
     case BUILT_IN_ATOMIC_EXCHANGE:
     case BUILT_IN_ATOMIC_COMPARE_EXCHANGE:
     case BUILT_IN_ATOMIC_LOAD:
diff --git a/gcc/config/aarch64/aarch64-builtins.c b/gcc/config/aarch64/aarch64-builtins.c
index 31a9167111c..06a45428c8d 100644
--- a/gcc/config/aarch64/aarch64-builtins.c
+++ b/gcc/config/aarch64/aarch64-builtins.c
@@ -478,40 +478,13 @@ enum aarch64_builtins
   AARCH64_BUILTIN_RSQRT_V2SF,
   AARCH64_BUILTIN_RSQRT_V4SF,
 
-  AARCH64_MORELLO_BUILTIN_ADDRESS_GET,
-  AARCH64_MORELLO_BUILTIN_ADDRESS_SET,
-  AARCH64_MORELLO_BUILTIN_BASE_GET,
-  AARCH64_MORELLO_BUILTIN_BOUNDS_SET,
-  AARCH64_MORELLO_BUILTIN_BOUNDS_SET_EXACT,
-  AARCH64_MORELLO_BUILTIN_CAP_BUILD,
-  AARCH64_MORELLO_BUILTIN_CAP_TYPE_COPY,
-  AARCH64_MORELLO_BUILTIN_COND_SEAL,
-  AARCH64_MORELLO_BUILTIN_COPY_FROM_HIGH,
-  AARCH64_MORELLO_BUILTIN_COPY_TO_HIGH,
-  AARCH64_MORELLO_BUILTIN_EQUAL_EXACT,
-  AARCH64_MORELLO_BUILTIN_FLAGS_GET,
-  AARCH64_MORELLO_BUILTIN_FLAGS_SET,
   AARCH64_MORELLO_BUILTIN_GLOBAL_DATA_GET,
-  AARCH64_MORELLO_BUILTIN_LENGTH_GET,
-  AARCH64_MORELLO_BUILTIN_OFFSET_GET,
-  AARCH64_MORELLO_BUILTIN_OFFSET_INC,
-  AARCH64_MORELLO_BUILTIN_OFFSET_SET,
   AARCH64_MORELLO_BUILTIN_PC_GET,
-  AARCH64_MORELLO_BUILTIN_PERMS_AND,
-  AARCH64_MORELLO_BUILTIN_PERMS_GET,
   AARCH64_MORELLO_BUILTIN_PTR_TO_CAP_OFFSET,
   AARCH64_MORELLO_BUILTIN_REPR_ALIGNMENT_MASK,
   AARCH64_MORELLO_BUILTIN_ROUND_REPR_LEN,
-  AARCH64_MORELLO_BUILTIN_SEAL,
-  AARCH64_MORELLO_BUILTIN_SEAL_ENTRY,
-  AARCH64_MORELLO_BUILTIN_SEALED_GET,
   AARCH64_MORELLO_BUILTIN_SUBSET_CHECK,
-  AARCH64_MORELLO_BUILTIN_SUBSET_TEST,
   AARCH64_MORELLO_BUILTIN_SUBSET_TEST_UNSEAL_OR_NULL,
-  AARCH64_MORELLO_BUILTIN_TAG_CLEAR,
-  AARCH64_MORELLO_BUILTIN_TAG_GET,
-  AARCH64_MORELLO_BUILTIN_TYPE_GET,
-  AARCH64_MORELLO_BUILTIN_UNSEAL,
 
   AARCH64_SIMD_BUILTIN_BASE,
   AARCH64_SIMD_BUILTIN_LANE_CHECK,
@@ -1472,81 +1445,9 @@ aarch64_init_morello_builtins (void)
                                                     true);
 
   morello_builtins_data data[] = {
-    {"__builtin_cheri_address_get",
-     AARCH64_MORELLO_BUILTIN_ADDRESS_GET,
-     build_function_type_list (size_type_node, cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_address_set",
-     AARCH64_MORELLO_BUILTIN_ADDRESS_SET,
-     build_function_type_list (cap_type_node, cap_type_node, size_type_node,
-                               NULL_TREE)},
-    {"__builtin_cheri_base_get",
-     AARCH64_MORELLO_BUILTIN_BASE_GET,
-     build_function_type_list (size_type_node, cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_bounds_set",
-     AARCH64_MORELLO_BUILTIN_BOUNDS_SET,
-     build_function_type_list (cap_type_node, cap_type_node, size_type_node,
-                               NULL_TREE)},
-    {"__builtin_cheri_bounds_set_exact",
-     AARCH64_MORELLO_BUILTIN_BOUNDS_SET_EXACT,
-     build_function_type_list (cap_type_node, cap_type_node, size_type_node,
-                               NULL_TREE)},
-    {"__builtin_cheri_cap_build",
-     AARCH64_MORELLO_BUILTIN_CAP_BUILD,
-     build_function_type_list (cap_type_node, cap_type_node,
-                               uintcap_type_node, NULL_TREE)},
-    {"__builtin_cheri_cap_type_copy",
-     AARCH64_MORELLO_BUILTIN_CAP_TYPE_COPY,
-     build_function_type_list (cap_type_node, cap_type_node,
-                               cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_conditional_seal",
-     AARCH64_MORELLO_BUILTIN_COND_SEAL,
-     build_function_type_list (cap_type_node, cap_type_node,
-                               cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_copy_from_high",
-     AARCH64_MORELLO_BUILTIN_COPY_FROM_HIGH,
-     build_function_type_list (size_type_node, cap_type_node,
-                               NULL_TREE)},
-    {"__builtin_cheri_copy_to_high",
-     AARCH64_MORELLO_BUILTIN_COPY_TO_HIGH,
-     build_function_type_list (cap_type_node, cap_type_node,
-                               size_type_node, NULL_TREE)},
-    {"__builtin_cheri_equal_exact",
-     AARCH64_MORELLO_BUILTIN_EQUAL_EXACT,
-     build_function_type_list (boolean_type_node, cap_type_node,
-                               cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_flags_get",
-     AARCH64_MORELLO_BUILTIN_FLAGS_GET,
-     build_function_type_list (size_type_node, cap_type_node,
-                               NULL_TREE)},
-    {"__builtin_cheri_flags_set",
-     AARCH64_MORELLO_BUILTIN_FLAGS_SET,
-     build_function_type_list (cap_type_node, cap_type_node,
-                               size_type_node, NULL_TREE)},
     {"__builtin_cheri_global_data_get",
      AARCH64_MORELLO_BUILTIN_GLOBAL_DATA_GET,
      build_function_type_list (cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_length_get",
-     AARCH64_MORELLO_BUILTIN_LENGTH_GET,
-     build_function_type_list (size_type_node, cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_offset_get",
-     AARCH64_MORELLO_BUILTIN_OFFSET_GET,
-     build_function_type_list (size_type_node, cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_offset_increment",
-     AARCH64_MORELLO_BUILTIN_OFFSET_INC,
-     build_function_type_list (cap_type_node, cap_type_node, size_type_node,
-                               NULL_TREE)},
-    {"__builtin_cheri_offset_set",
-     AARCH64_MORELLO_BUILTIN_OFFSET_SET,
-     build_function_type_list (cap_type_node, cap_type_node, size_type_node,
-                               NULL_TREE)},
-    {"__builtin_cheri_perms_and",
-     AARCH64_MORELLO_BUILTIN_PERMS_AND,
-     build_function_type_list (cap_type_node, cap_type_node, size_type_node,
-                               NULL_TREE)},
-    {"__builtin_cheri_perms_get",
-     AARCH64_MORELLO_BUILTIN_PERMS_GET,
-     build_function_type_list (size_type_node, cap_type_node,
-                               NULL_TREE)},
     {"__builtin_cheri_program_counter_get",
      AARCH64_MORELLO_BUILTIN_PC_GET,
      build_function_type_list (cap_type_node, NULL_TREE)},
@@ -1556,38 +1457,6 @@ aarch64_init_morello_builtins (void)
     {"__builtin_cheri_round_representable_length",
      AARCH64_MORELLO_BUILTIN_ROUND_REPR_LEN,
      build_function_type_list (size_type_node, size_type_node, NULL_TREE)},
-    {"__builtin_cheri_seal",
-     AARCH64_MORELLO_BUILTIN_SEAL,
-      build_function_type_list (cap_type_node, const_cap_type_node,
-                                cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_seal_entry",
-     AARCH64_MORELLO_BUILTIN_SEAL_ENTRY,
-      build_function_type_list (cap_type_node, cap_type_node,
-                                NULL_TREE)},
-    {"__builtin_cheri_sealed_get",
-     AARCH64_MORELLO_BUILTIN_SEALED_GET,
-      build_function_type_list (boolean_type_node, cap_type_node,
-                                NULL_TREE)},
-    {"__builtin_cheri_subset_test",
-     AARCH64_MORELLO_BUILTIN_SUBSET_TEST,
-      build_function_type_list (boolean_type_node, cap_type_node,
-                                cap_type_node, NULL_TREE)},
-    {"__builtin_cheri_tag_clear",
-     AARCH64_MORELLO_BUILTIN_TAG_CLEAR,
-      build_function_type_list (cap_type_node, cap_type_node,
-                                NULL_TREE)},
-    {"__builtin_cheri_tag_get",
-     AARCH64_MORELLO_BUILTIN_TAG_GET,
-      build_function_type_list (boolean_type_node, cap_type_node,
-                                NULL_TREE)},
-    {"__builtin_cheri_type_get",
-     AARCH64_MORELLO_BUILTIN_TYPE_GET,
-      build_function_type_list (ptrdiff_type_node, cap_type_node,
-                                NULL_TREE)},
-    {"__builtin_cheri_unseal",
-     AARCH64_MORELLO_BUILTIN_UNSEAL,
-      build_function_type_list (cap_type_node, cap_type_node,
-                                cap_type_node, NULL_TREE)},
     {"__builtin_morello_chkssu",
      AARCH64_MORELLO_BUILTIN_SUBSET_CHECK,
       build_function_type_list (cap_type_node, const_cap_type_node,
@@ -2257,188 +2126,12 @@ aarch64_expand_morello_builtin (tree exp, rtx target, int fcode)
 
   switch (fcode)
     {
-    case AARCH64_MORELLO_BUILTIN_ADDRESS_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_address_get, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_ADDRESS_SET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, DImode);
-        expand_insn (CODE_FOR_replace_address_value_cadi, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_BASE_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_base_get, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_BOUNDS_SET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, DImode);
-        expand_insn (CODE_FOR_aarch64_cap_bounds_set, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_BOUNDS_SET_EXACT:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, DImode);
-        expand_insn (CODE_FOR_aarch64_cap_bounds_set_exact, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_CAP_BUILD:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_build, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_CAP_TYPE_COPY:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_type_copy, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_COND_SEAL:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_cond_seal, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_COPY_FROM_HIGH:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_copy_from_high, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_COPY_TO_HIGH:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, DImode);
-        expand_insn (CODE_FOR_aarch64_cap_copy_to_high, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_EQUAL_EXACT:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, SImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_equal_exact, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_FLAGS_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_flags_get, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_FLAGS_SET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, DImode);
-        expand_insn (CODE_FOR_aarch64_cap_flags_set, 3, ops);
-        return ops[0].value;
-      }
     case AARCH64_MORELLO_BUILTIN_GLOBAL_DATA_GET:
       {
         create_output_operand (&ops[0], target, CADImode);
 	expand_insn (CODE_FOR_cap_global_data_get, 1, ops);
         return ops[0].value;
       }
-    case AARCH64_MORELLO_BUILTIN_LENGTH_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_length_get, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_OFFSET_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_offset_get, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_OFFSET_INC:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        scalar_addr_mode mode = as_a <scalar_addr_mode> (GET_MODE (target));
-        rtx expand_plus = expand_pointer_plus (mode, op0, op1, target, 1,
-                                               OPTAB_WIDEN);
-        emit_move_insn (target, expand_plus);
-        return expand_plus;
-      }
-    case AARCH64_MORELLO_BUILTIN_OFFSET_SET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, DImode);
-        expand_insn (CODE_FOR_aarch64_cap_offset_set, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_PERMS_AND:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, DImode);
-        expand_insn (CODE_FOR_aarch64_cap_perms_and, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_PERMS_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_perms_get, 2, ops);
-        return ops[0].value;
-      }
     case AARCH64_MORELLO_BUILTIN_PC_GET:
       {
         create_output_operand (&ops[0], target, CADImode);
@@ -2471,32 +2164,6 @@ aarch64_expand_morello_builtin (tree exp, rtx target, int fcode)
         expand_insn (CODE_FOR_aarch64_cap_round_repr_len, 2, ops);
         return ops[0].value;
       }
-    case AARCH64_MORELLO_BUILTIN_SEAL:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_seal, 3, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_SEAL_ENTRY:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_seal_entry, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_SEALED_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_sealed_get, 2, ops);
-        return ops[0].value;
-      }
     case AARCH64_MORELLO_BUILTIN_SUBSET_CHECK:
       {
         rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
@@ -2507,16 +2174,6 @@ aarch64_expand_morello_builtin (tree exp, rtx target, int fcode)
         expand_insn (CODE_FOR_aarch64_cap_subset_check_cadi, 3, ops);
         return ops[0].value;
       }
-    case AARCH64_MORELLO_BUILTIN_SUBSET_TEST:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, SImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_subset_test, 3, ops);
-        return ops[0].value;
-      }
     case AARCH64_MORELLO_BUILTIN_SUBSET_TEST_UNSEAL_OR_NULL:
       {
         rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
@@ -2528,40 +2185,6 @@ aarch64_expand_morello_builtin (tree exp, rtx target, int fcode)
                      3, ops);
         return ops[0].value;
       }
-    case AARCH64_MORELLO_BUILTIN_TAG_CLEAR:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_tag_clear, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_TAG_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_tag_get, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_TYPE_GET:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        create_output_operand (&ops[0], target, DImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_type_get, 2, ops);
-        return ops[0].value;
-      }
-    case AARCH64_MORELLO_BUILTIN_UNSEAL:
-      {
-        rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
-        rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
-        create_output_operand (&ops[0], target, CADImode);
-        create_input_operand (&ops[1], op0, CADImode);
-        create_input_operand (&ops[2], op1, CADImode);
-        expand_insn (CODE_FOR_aarch64_cap_unseal, 3, ops);
-        return ops[0].value;
-      }
     }
 
   gcc_unreachable ();
@@ -2675,40 +2298,13 @@ aarch64_general_expand_builtin (unsigned int fcode, tree exp, rtx target,
       emit_insn (GEN_FCN (CODE_FOR_aarch64_fjcvtzs) (target, op0));
       return target;
 
-    case AARCH64_MORELLO_BUILTIN_ADDRESS_GET:
-    case AARCH64_MORELLO_BUILTIN_ADDRESS_SET:
-    case AARCH64_MORELLO_BUILTIN_BASE_GET:
-    case AARCH64_MORELLO_BUILTIN_BOUNDS_SET:
-    case AARCH64_MORELLO_BUILTIN_BOUNDS_SET_EXACT:
-    case AARCH64_MORELLO_BUILTIN_CAP_BUILD:
-    case AARCH64_MORELLO_BUILTIN_CAP_TYPE_COPY:
-    case AARCH64_MORELLO_BUILTIN_COND_SEAL:
-    case AARCH64_MORELLO_BUILTIN_COPY_FROM_HIGH:
-    case AARCH64_MORELLO_BUILTIN_COPY_TO_HIGH:
-    case AARCH64_MORELLO_BUILTIN_EQUAL_EXACT:
-    case AARCH64_MORELLO_BUILTIN_FLAGS_GET:
-    case AARCH64_MORELLO_BUILTIN_FLAGS_SET:
     case AARCH64_MORELLO_BUILTIN_GLOBAL_DATA_GET:
-    case AARCH64_MORELLO_BUILTIN_LENGTH_GET:
-    case AARCH64_MORELLO_BUILTIN_OFFSET_INC:
-    case AARCH64_MORELLO_BUILTIN_OFFSET_GET:
-    case AARCH64_MORELLO_BUILTIN_OFFSET_SET:
     case AARCH64_MORELLO_BUILTIN_PC_GET:
-    case AARCH64_MORELLO_BUILTIN_PERMS_AND:
-    case AARCH64_MORELLO_BUILTIN_PERMS_GET:
     case AARCH64_MORELLO_BUILTIN_PTR_TO_CAP_OFFSET:
     case AARCH64_MORELLO_BUILTIN_REPR_ALIGNMENT_MASK:
     case AARCH64_MORELLO_BUILTIN_ROUND_REPR_LEN:
-    case AARCH64_MORELLO_BUILTIN_SEAL:
-    case AARCH64_MORELLO_BUILTIN_SEALED_GET:
-    case AARCH64_MORELLO_BUILTIN_SEAL_ENTRY:
     case AARCH64_MORELLO_BUILTIN_SUBSET_CHECK:
-    case AARCH64_MORELLO_BUILTIN_SUBSET_TEST:
     case AARCH64_MORELLO_BUILTIN_SUBSET_TEST_UNSEAL_OR_NULL:
-    case AARCH64_MORELLO_BUILTIN_TAG_CLEAR:
-    case AARCH64_MORELLO_BUILTIN_TAG_GET:
-    case AARCH64_MORELLO_BUILTIN_TYPE_GET:
-    case AARCH64_MORELLO_BUILTIN_UNSEAL:
       return aarch64_expand_morello_builtin (exp, target, fcode);
     case AARCH64_SIMD_BUILTIN_FCMLA_LANEQ0_V2SF:
     case AARCH64_SIMD_BUILTIN_FCMLA_LANEQ90_V2SF:
diff --git a/gcc/config/aarch64/aarch64-morello.md b/gcc/config/aarch64/aarch64-morello.md
index a27234b220d..956e968bde6 100644
--- a/gcc/config/aarch64/aarch64-morello.md
+++ b/gcc/config/aarch64/aarch64-morello.md
@@ -179,7 +179,7 @@
 
 ;; CHERI builtins helpers.
 
-(define_insn "aarch64_cap_base_get"
+(define_insn "cap_base_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_BASE_GET))
@@ -188,7 +188,7 @@
   "gcbase\\t%0, %1"
 )
 
-(define_insn "aarch64_cap_length_get"
+(define_insn "cap_length_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_LEN_GET))
@@ -215,7 +215,7 @@
   "rrlen\\t%0, %1"
 )
 
-(define_insn "aarch64_cap_bounds_set"
+(define_insn "cap_bounds_set_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "rk")
           (match_operand:DI 2 "register_operand" "r")]
@@ -225,7 +225,7 @@
   "scbnds\\t%0, %1, %2"
 )
 
-(define_insn "aarch64_cap_bounds_set_exact"
+(define_insn "cap_bounds_set_exact_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "rk")
           (match_operand:DI 2 "register_operand" "r")]
@@ -235,7 +235,7 @@
   "scbndse\\t%0, %1, %2"
 )
 
-(define_insn "aarch64_cap_seal"
+(define_insn "cap_seal_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "rk")
           (match_operand:CADI 2 "register_operand" "rk")]
@@ -263,7 +263,7 @@
   "mrs\\t%0, DDC"
 )
 
-(define_expand "aarch64_cap_perms_and"
+(define_expand "cap_perms_and_cadi"
   [(match_operand:CADI 0 "register_operand")
    (match_operand:CADI 1 "register_operand")
    (match_operand:DI 2 "register_operand")]
@@ -275,7 +275,7 @@
   }
 )
 
-(define_insn "aarch64_cap_offset_get"
+(define_insn "cap_offset_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_OFFSET_GET))
@@ -284,7 +284,7 @@
   "gcoff\\t%0, %1"
 )
 
-(define_insn "aarch64_cap_offset_set"
+(define_insn "cap_offset_set_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "rk")
           (match_operand:DI 2 "register_operand" "r")]
@@ -294,7 +294,7 @@
   "scoff\\t%0, %1, %2"
 )
 
-(define_insn "aarch64_cap_address_get"
+(define_insn "cap_address_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_ADDR_GET))
@@ -303,7 +303,7 @@
   "gcvalue\\t%0, %1"
 )
 
-(define_insn "aarch64_cap_tag_get"
+(define_insn "cap_tag_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_TAG_GET))
@@ -312,7 +312,7 @@
   "gctag\\t%0, %1"
 )
 
-(define_insn "aarch64_cap_tag_clear"
+(define_insn "cap_tag_clear_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_TAG_CLEAR))
@@ -321,7 +321,7 @@
   "clrtag\\t%0, %1"
 )
 
-(define_insn "aarch64_cap_build"
+(define_insn "cap_build_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "rk")
           (match_operand:CADI 2 "register_operand" "rk")]
@@ -331,7 +331,7 @@
   "build\\t%0, %2, %1"
 )
 
-(define_insn "aarch64_cap_cond_seal"
+(define_insn "cap_conditional_seal_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "rk")
           (match_operand:CADI 2 "register_operand" "rk")]
@@ -341,7 +341,7 @@
   "cseal\\t%0, %1, %2"
 )
 
-(define_insn "aarch64_cap_type_copy"
+(define_insn "cap_type_copy_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=r")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "r")
           (match_operand:CADI 2 "register_operand" "r")]
@@ -361,7 +361,7 @@
   "chkeq\\t%0, %1"
 )
 
-(define_expand "aarch64_cap_equal_exact"
+(define_expand "cap_equal_exact_cadi"
   [(match_operand:SI 0 "register_operand")
    (match_operand:CADI 1 "register_operand")
    (match_operand:CADI 2 "register_operand")]
@@ -376,7 +376,7 @@
   }
 )
 
-(define_insn "aarch64_cap_flags_set"
+(define_insn "cap_flags_set_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "rk")
           (match_operand:DI 2 "register_operand" "r")]
@@ -386,7 +386,7 @@
   "scflgs\\t%0, %1, %2"
 )
 
-(define_insn "aarch64_cap_flags_get"
+(define_insn "cap_flags_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_FLAGS_GET))
@@ -395,7 +395,7 @@
   "gcflgs\\t%0, %1"
 )
 
-(define_insn "aarch64_cap_perms_get"
+(define_insn "cap_perms_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_PERMS_GET))
@@ -412,7 +412,7 @@
   "adr\\t%0, #0"
 )
 
-(define_insn "aarch64_cap_seal_entry"
+(define_insn "cap_seal_entry_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=r")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "r")]
             UNSPEC_CHERI_SEAL_ENTRY))
@@ -421,7 +421,7 @@
   "seal\\t%0, %1, rb"
 )
 
-(define_insn "aarch64_cap_sealed_get"
+(define_insn "cap_sealed_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_SEALED_GET))
@@ -440,7 +440,7 @@
   "chkss\\t%0, %1"
 )
 
-(define_expand "aarch64_cap_subset_test"
+(define_expand "cap_subset_test_cadi"
   [(match_operand:SI 0 "register_operand")
    (match_operand:CADI 1 "register_operand")
    (match_operand:CADI 2 "register_operand")]
@@ -455,7 +455,7 @@
   }
 )
 
-(define_insn "aarch64_cap_type_get"
+(define_insn "cap_type_get_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
             UNSPEC_CHERI_TYPE_GET))
@@ -464,7 +464,7 @@
   "gctype\\t%0, %1"
 )
 
-(define_insn "aarch64_cap_unseal"
+(define_insn "cap_unseal_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=r")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "r")
           (match_operand:CADI 2 "register_operand" "r")]
@@ -474,7 +474,7 @@
   "unseal\\t%0, %1, %2"
 )
 
-(define_insn "aarch64_cap_copy_to_high"
+(define_insn "cap_copy_to_high_cadi"
   [(set (match_operand:CADI 0 "register_operand" "=rk")
         (unspec:CADI [(match_operand:CADI 1 "register_operand" "r")
           (match_operand:DI 2 "register_operand" "r")]
@@ -484,10 +484,10 @@
   "cthi\\t%0, %1, %2"
 )
 
-(define_insn "aarch64_cap_copy_from_high"
+(define_insn "cap_copy_from_high_cadi"
   [(set (match_operand:DI 0 "register_operand" "=r")
         (unspec:DI [(match_operand:CADI 1 "register_operand" "rk")]
-            UNSPEC_CHERI_COPY_TO_HIGH))
+            UNSPEC_CHERI_COPY_FROM_HIGH))
   ]
   "TARGET_MORELLO"
   "cfhi\\t%0, %1"
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 3e151788d7b..f8eeac5f33f 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -1346,7 +1346,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
 	  /* Don't try to apply restrict to void.  */
 	  int quals = cp_type_quals (nfrom) & ~TYPE_QUAL_RESTRICT;
 	  from_pointee = cp_build_qualified_type (void_type_node, quals);
-	  from = build_pointer_type (from_pointee);
+	  from = change_pointer_target_type (from, from_pointee);
 	  conv = build_conv (ck_ptr, from, conv);
 	}
       else if (TYPE_PTRDATAMEM_P (from))
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index f60c435d716..9d3edb92baa 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -1533,7 +1533,7 @@ strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
     {
     case POINTER_TYPE:
       type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
-      result = build_pointer_type (type);
+      result = change_pointer_target_type (t, type);
       break;
     case REFERENCE_TYPE:
       type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 63eaff17864..75314cbdd1e 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -7715,6 +7715,221 @@ Operand 2 must be a power of two.
 Similar to @samp{align_up_@var{m}}, except that this pattern aligns the address
 up rather than down to the power of two provided in operand 2.
 
+@cindex @code{cap_address_get_@var{m}} instruction pattern
+@item @samp{cap_address_get_@var{m}}
+Set operand 0 to the address held in capability operand 1.  Operand 1 has
+capability mode @var{m} and operand 0 has the corresponding offset mode.
+
+@cindex @code{cap_base_get_@var{m}} instruction pattern
+@item @samp{cap_base_get_@var{m}}
+Set operand 0 to the lower bound of capability operand 1.  Operand 1 has
+capability mode @var{m} and operand 0 has the corresponding offset mode.
+
+@cindex @code{cap_bounds_set_@var{m}} instruction pattern
+@item @samp{cap_bounds_set_@var{m}}
+Use operand 1 to derive a capability with the following properties:
+
+@itemize @bullet
+@item
+The lower bound is the address in operand 1.
+
+@item
+The address is the same as in operand 1.
+
+@item
+the length of the bounds is at least operand 2.
+@end itemize
+
+Store the result in operand 0.
+
+Operands 0 and 1 have capability mode @var{m} and operand 2 has the
+corresponding offset mode.
+
+@cindex @code{cap_bounds_set_exact_@var{m}} instruction pattern
+@item @samp{cap_bounds_set_exact_@var{m}}
+Like @samp{cap_bounds_set_@var{m}}, but clear the tag if the length
+of the bounds cannot be set to exactly operand 2.
+
+@cindex @code{cap_build_@var{m}} instruction pattern
+@item @samp{cap_build_@var{m}}
+Perform the following procedure:
+
+@itemize @bullet
+@item
+Take a temporary copy T of capability operand 1.
+
+@item
+If T is sealed, unseal it and clear its tag.
+
+@item
+Set T's tag bit if capability operand 2 is unsealed and if T
+is a subset of operand 2.
+
+@item
+Store T in operand 0.
+@end itemize
+
+All three operands have capability mode @var{m}.
+
+@cindex @code{cap_conditionally_seal_@var{m}} instruction pattern
+@item @samp{cap_conditionally_seal_@var{m}}
+Try to use capability operand 2 to seal capability operand 1.  Store the
+result in operand 0 on success, otherwise copy operand 1 to operand 0
+unmodified (preserving its tag).
+
+All three operands have capability mode @var{m}.
+
+@cindex @code{cap_copy_from_high_@var{m}} instruction pattern
+@item @samp{cap_copy_from_high_@var{m}}
+Set operand 0 to the upper (non-value, non-tag) bits of capability operand 1.
+Operand 1 has capability mode @var{m} and operand 0 has an integer
+mode of the target's choosing.
+
+@cindex @code{cap_copy_to_high_@var{m}} instruction pattern
+@item @samp{cap_copy_to_high_@var{m}}
+Set operand 0 to a capability that has the following properties:
+
+@itemize @bullet
+@item
+The tag bit is clear.
+
+@item
+The low (value) bits are the same as those in capability operand 1.
+
+@item
+The high (non-value, non-tag) bits are those specified by integer operand 2,
+shifted into place.
+@end itemize
+
+Operands 0 and 1 have capability mode @var{m}.  Operand 2 has an
+integer mode of the target's choosing.
+
+@cindex @code{cap_equal_exact_@var{m}} instruction pattern
+@item @samp{cap_equal_exact_@var{m}}
+Set operand 0 to true if capability operands 1 and 2 are bit-for-bit
+identical (including the tag bits).  Set operand 0 to false otherwise.
+
+Operands 1 and 2 have capability mode @var{m}.  Operand 0 has an integer
+mode of the target's choosing.
+
+@cindex @code{cap_flags_get_@var{m}} instruction pattern
+@item @samp{cap_flags_get_@var{m}}
+Set operand 0 to the flags of capability operand 1.  Operand 1 has
+capability mode @var{m} and operand 0 has an integer mode of the
+target's choosing.
+
+@cindex @code{cap_flags_set_@var{m}} instruction pattern
+@item @samp{cap_flags_set_@var{m}}
+Set operand 0 to a copy of capability operand 1, but with the flags
+replaced by those in integer operand 2 (shifted into place).  Clear the
+tag of the result if the capability is sealed.
+
+Operands 0 and 1 have capability mode @var{m}.  Operand 2 has an
+integer mode of the target's choosing.
+
+@cindex @code{cap_length_get_@var{m}} instruction pattern
+@item @samp{cap_length_get_@var{m}}
+Set operand 0 to the length of the bounds of capability operand 1.
+Operand 1 has capability mode @var{m} and operand 0 has the corresponding
+offset mode.
+
+Adding the result of this operation to the result of
+@samp{cap_base_get_@var{m}} gives the (exclusive) upper bound
+of the capability.
+
+@cindex @code{cap_offset_get_@var{m}} instruction pattern
+@item @samp{cap_offset_get_@var{m}}
+Subtract the base of capability operand 1's bounds from its address
+and store the result in operand 0.  Operand 1 has capability mode
+@var{m} and operand 0 has the corresponding offset mode.
+
+@cindex @code{cap_perms_and_@var{m}} instruction pattern
+@item @samp{cap_perms_and_@var{m}}
+Copy capability operand 1 to operand 0, but AND the resulting permission
+bits with integer operand 2 (shifted into place).  The meaning of each bit
+of operand 2 is target-dependent, but the bit positions must be
+consistent with those defined in @file{cheriintrin.h}.
+
+Operands 0 and 1 have capability mode @var{m}.  Operand 2 has an integer
+mode of the target's choosing.
+
+@cindex @code{cap_perms_get_@var{m}} instruction pattern
+@item @samp{cap_perms_get_@var{m}}
+Set operand 0 to the permissions of capability operand 1.  The meaning
+of each bit of operand 0 is target-dependent, but the bit positions must
+be consistent with those defined in @file{cheriintrin.h}.
+
+Operand 1 has capability mode @var{m} and operand 0 has an integer
+mode of the target's choosing.
+
+@cindex @code{cap_seal_@var{m}} instruction pattern
+@item @samp{cap_seal_@var{m}}
+Use capability operand 2 to seal capability operand 1 and store the
+result in operand 0.  All three operands have capability mode @var{m}.
+
+@cindex @code{cap_seal_entry_@var{m}} instruction pattern
+@item @samp{cap_seal_entry_@var{m}}
+Seal capability operand 1 for use in an indirect branch and store the
+result in operand 0.  Both operands have capability mode @var{m}.
+
+@cindex @code{cap_sealed_get_@var{m}} instruction pattern
+@item @samp{cap_sealed_get_@var{m}}
+Set operand 0 to true if capability operand 1 is sealed.  Set operand 0
+to false otherwise.
+
+Operand 1 has capability mode @var{m} and operand 0 has an integer mode
+of the target's choosing.
+
+@cindex @code{cap_subset_test_@var{m}} instruction pattern
+@item @samp{cap_subset_test_@var{m}}
+Set operand 0 to true if the tag bits of capability operands 1 and 2
+are equal and if operand 1 is a subset of operand 2.  Set operand 0
+to false otherwise.
+
+Operands 1 and 2 have capability mode @var{m}.  Operand 0 has an integer
+mode of the target's choosing.
+
+@cindex @code{cap_tag_clear_@var{m}} instruction pattern
+@item @samp{cap_tag_clear_@var{m}}
+Set operand 0 to an untagged copy of capability operand 1.
+Both operands have capability mode @var{m}.
+
+@cindex @code{cap_tag_get_@var{m}} instruction pattern
+@item @samp{cap_tag_get_@var{m}}
+Set operand 0 to true if capability operand 1's tag bit is set.  Set operand 0
+to false otherwise.
+
+Operand 1 has capability mode @var{m} and operand 0 has an integer mode
+of the target's choosing.
+
+@cindex @code{cap_type_copy_@var{m}} instruction pattern
+@item @samp{cap_type_copy_@var{m}}
+Perform the following procedure:
+
+@itemize @bullet
+@item
+Use capability operand 1 to derive a temporary capability T,
+setting T's value to the object type of capability operand 2.
+
+@item
+Clear T's tag if operand 1 is sealed.
+
+@item
+Store T in operand 0.
+@end itemize
+
+All three operands have capability mode @var{m}.
+
+@cindex @code{cap_type_get_@var{m}} instruction pattern
+@item @samp{cap_type_get_@var{m}}
+Set operand 0 to the object type of capability operand 1.  Operand 1 has
+capability mode @var{m} and operand 0 has an integer mode of the
+target's choosing.
+
+@cindex @code{cap_unseal_@var{m}} instruction pattern
+@item @samp{cap_unseal_@var{m}}
+Use capability operand 2 to unseal capability operand 1 and store the
+result in capability operand 0.
 @end table
 
 @end ifset
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index cebb492dc5a..5bda32225ed 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -67,6 +67,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dbgcnt.h"
 #include "omp-offload.h"
 #include "context.h"
+#include "internal-fn.h"
 
 /* Hash set of poisoned variables in a bind expr.  */
 static hash_set<tree> *asan_poisoned_variables = NULL;
@@ -3282,6 +3283,36 @@ maybe_fold_stmt (gimple_stmt_iterator *gsi)
   return fold_stmt (gsi);
 }
 
+/* If call expression EXPR should be gimplified as a call to an internal
+   function, return that internal function, otherwise return IFN_LAST.  */
+
+static internal_fn
+gimplify_internal_fn (tree expr)
+{
+  if (!CALL_EXPR_FN (expr))
+    return CALL_EXPR_IFN (expr);
+
+  tree fndecl = get_callee_fndecl (expr);
+  if (fndecl && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+    {
+      built_in_function code = DECL_FUNCTION_CODE (fndecl);
+
+      if (code == BUILT_IN_CHERI_ADDRESS_SET
+	  && targetm.capability_mode ().exists ())
+	return IFN_REPLACE_ADDRESS_VALUE;
+
+      internal_fn ifn = direct_cheri_internal_fn (code);
+      if (ifn != IFN_LAST)
+	{
+	  tree_pair types = direct_internal_fn_types (ifn, TREE_TYPE (fndecl));
+	  if (direct_internal_fn_supported_p (ifn, types, OPTIMIZE_FOR_BOTH))
+	    return ifn;
+	}
+    }
+
+  return IFN_LAST;
+}
+
 /* Gimplify the CALL_EXPR node *EXPR_P into the GIMPLE sequence PRE_P.
    WANT_VALUE is true if the result of the call is desired.  */
 
@@ -3303,13 +3334,17 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
     SET_EXPR_LOCATION (*expr_p, input_location);
 
   /* Gimplify internal functions created in the FEs.  */
-  if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
+  internal_fn ifn = gimplify_internal_fn (*expr_p);
+  if (ifn != IFN_LAST)
     {
       if (want_value)
-	return GS_ALL_DONE;
+	{
+	  CALL_EXPR_FN (*expr_p) = NULL_TREE;
+	  CALL_EXPR_IFN (*expr_p) = ifn;
+	  return GS_ALL_DONE;
+	}
 
       nargs = call_expr_nargs (*expr_p);
-      enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
       auto_vec<tree> vargs (nargs);
 
       for (i = 0; i < nargs; i++)
@@ -3374,6 +3409,12 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
 	cfun->calls_eh_return = true;
 	break;
 
+      case BUILT_IN_CHERI_OFFSET_INCREMENT:
+	*expr_p = fold_build_pointer_plus_loc (EXPR_LOCATION (*expr_p),
+					       CALL_EXPR_ARG (*expr_p, 0),
+					       CALL_EXPR_ARG (*expr_p, 1));
+	return GS_OK;
+
       default:
         ;
       }
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 9a1e8878cb6..cdf51ccd732 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -3235,6 +3235,23 @@ direct_internal_fn_types (internal_fn fn, gcall *call)
   return tree_pair (TREE_TYPE (op0), TREE_TYPE (op1));
 }
 
+/* FNTYPE is a function type whose signature is compatible with FN
+   (which satisfies direct_internal_fn_p).  Return the types that
+   should be used to determine whether the target supports FN.  */
+
+tree_pair
+direct_internal_fn_types (internal_fn fn, tree fntype)
+{
+  const direct_internal_fn_info &info = direct_internal_fn (fn);
+  tree type0 = (info.type0 < 0
+		? TREE_TYPE (fntype)
+		: type_argument_type (fntype, info.type0));
+  tree type1 = (info.type1 < 0
+		? TREE_TYPE (fntype)
+		: type_argument_type (fntype, info.type1));
+  return tree_pair (type0, type1);
+}
+
 /* Return true if OPTAB is supported for TYPES (whose modes should be
    the same) when the optimization type is OPT_TYPE.  Used for simple
    direct optabs.  */
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index a7a280894fb..2b54f15dfc6 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -398,6 +398,57 @@ DEF_INTERNAL_FN (REPLACE_ADDRESS_VALUE, ECF_CONST | ECF_LEAF | ECF_NOTHROW, ".RR
 
 DEF_INTERNAL_FN (CAP_GLOBAL_DATA_GET, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 
+DEF_INTERNAL_OPTAB_FN (CAP_ADDRESS_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_address_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_BASE_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_base_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_BOUNDS_SET, ECF_CONST | ECF_NOTHROW,
+		       cap_bounds_set, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_BOUNDS_SET_EXACT, ECF_CONST | ECF_NOTHROW,
+		       cap_bounds_set_exact, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_BUILD, ECF_CONST | ECF_NOTHROW,
+		       cap_build, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_CONDITIONAL_SEAL, ECF_CONST | ECF_NOTHROW,
+		       cap_conditional_seal, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_COPY_FROM_HIGH, ECF_CONST | ECF_NOTHROW,
+		       cap_copy_from_high, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_COPY_TO_HIGH, ECF_CONST | ECF_NOTHROW,
+		       cap_copy_to_high, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_EQUAL_EXACT, ECF_CONST | ECF_NOTHROW,
+		       cap_equal_exact, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_FLAGS_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_flags_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_FLAGS_SET, ECF_CONST | ECF_NOTHROW,
+		       cap_flags_set, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_LENGTH_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_length_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_OFFSET_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_offset_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_OFFSET_SET, ECF_CONST | ECF_NOTHROW,
+		       cap_offset_set, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_PERMS_AND, ECF_CONST | ECF_NOTHROW,
+		       cap_perms_and, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_PERMS_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_perms_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_SEAL, ECF_CONST | ECF_NOTHROW,
+		       cap_seal, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_SEAL_ENTRY, ECF_CONST | ECF_NOTHROW,
+		       cap_seal_entry, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_SEALED_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_sealed_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_SUBSET_TEST, ECF_CONST | ECF_NOTHROW,
+		       cap_subset_test, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_TAG_CLEAR, ECF_CONST | ECF_NOTHROW,
+		       cap_tag_clear, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_TAG_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_tag_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_TYPE_COPY, ECF_CONST | ECF_NOTHROW,
+		       cap_type_copy, binary)
+DEF_INTERNAL_OPTAB_FN (CAP_TYPE_GET, ECF_CONST | ECF_NOTHROW,
+		       cap_type_get, unary)
+DEF_INTERNAL_OPTAB_FN (CAP_UNSEAL, ECF_CONST | ECF_NOTHROW,
+		       cap_unseal, binary)
+
 /* A NOP function with arbitrary arguments and return value.  */
 DEF_INTERNAL_FN (NOP, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 
diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
index ac970e9e82d..b75aac268c1 100644
--- a/gcc/internal-fn.h
+++ b/gcc/internal-fn.h
@@ -183,6 +183,7 @@ direct_internal_fn (internal_fn fn)
 
 extern tree_pair direct_internal_fn_types (internal_fn, tree, tree *);
 extern tree_pair direct_internal_fn_types (internal_fn, gcall *);
+extern tree_pair direct_internal_fn_types (internal_fn, tree);
 extern bool direct_internal_fn_supported_p (internal_fn, tree_pair,
 					    optimization_type);
 extern bool direct_internal_fn_supported_p (internal_fn, tree,
diff --git a/gcc/optabs.def b/gcc/optabs.def
index 41dcb484803..9109da6bc5b 100644
--- a/gcc/optabs.def
+++ b/gcc/optabs.def
@@ -449,3 +449,29 @@ OPTAB_D (pointer_minus_optab, "pointer_minus_$a")
 OPTAB_D (align_down_optab, "align_down_$a")
 
 OPTAB_D (align_up_optab, "align_up_$a")
+
+OPTAB_D (cap_address_get_optab, "cap_address_get_$a")
+OPTAB_D (cap_base_get_optab, "cap_base_get_$a")
+OPTAB_D (cap_bounds_set_exact_optab, "cap_bounds_set_exact_$a")
+OPTAB_D (cap_bounds_set_optab, "cap_bounds_set_$a")
+OPTAB_D (cap_build_optab, "cap_build_$a")
+OPTAB_D (cap_conditional_seal_optab, "cap_conditional_seal_$a")
+OPTAB_D (cap_copy_from_high_optab, "cap_copy_from_high_$a")
+OPTAB_D (cap_copy_to_high_optab, "cap_copy_to_high_$a")
+OPTAB_D (cap_equal_exact_optab, "cap_equal_exact_$a")
+OPTAB_D (cap_flags_get_optab, "cap_flags_get_$a")
+OPTAB_D (cap_flags_set_optab, "cap_flags_set_$a")
+OPTAB_D (cap_length_get_optab, "cap_length_get_$a")
+OPTAB_D (cap_offset_get_optab, "cap_offset_get_$a")
+OPTAB_D (cap_offset_set_optab, "cap_offset_set_$a")
+OPTAB_D (cap_perms_and_optab, "cap_perms_and_$a")
+OPTAB_D (cap_perms_get_optab, "cap_perms_get_$a")
+OPTAB_D (cap_seal_entry_optab, "cap_seal_entry_$a")
+OPTAB_D (cap_seal_optab, "cap_seal_$a")
+OPTAB_D (cap_sealed_get_optab, "cap_sealed_get_$a")
+OPTAB_D (cap_subset_test_optab, "cap_subset_test_$a")
+OPTAB_D (cap_tag_clear_optab, "cap_tag_clear_$a")
+OPTAB_D (cap_tag_get_optab, "cap_tag_get_$a")
+OPTAB_D (cap_type_copy_optab, "cap_type_copy_$a")
+OPTAB_D (cap_type_get_optab, "cap_type_get_$a")
+OPTAB_D (cap_unseal_optab, "cap_unseal_$a")
diff --git a/gcc/testsuite/c-c++-common/cheri-overloads-1.c b/gcc/testsuite/c-c++-common/cheri-overloads-1.c
new file mode 100644
index 00000000000..d746be6afbd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cheri-overloads-1.c
@@ -0,0 +1,224 @@
+/* { dg-require-effective-target cheri_capability_any } */
+/* { dg-options "-W -Wall -Wconversion" } */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdint.h>
+
+struct s { size_t i; };
+
+int *__capability
+one_arg (int *__capability a)
+{
+  return __builtin_cheri_bounds_set (a); /* { dg-error {too few arguments} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+int *__capability
+three_args (int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b, 0); /* { dg-error {too many arguments} } */
+}
+
+int *__capability
+intp_good (int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+short *__capability
+shortp_good (short *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+struct s *__capability
+sp_good (struct s *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+int *__capability
+intp_int64_t (int *__capability a, int64_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {may change the sign} "" { xfail c++ } } */
+}
+
+int *__capability
+intp_uint64_t (int *__capability a, uint64_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+int *__capability
+intp_intcap (int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {may change the sign} "" { xfail c++ } } */
+}
+
+int *__capability
+intp_uintcap (int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+int *__capability
+intp_float (int *__capability a, float b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {may change value} } */
+}
+
+int *__capability
+intp_s (int *__capability a, struct s b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-error {(incompatible type|cannot convert)} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+__intcap_t
+intp_to_intcap (int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {integer from pointer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+int
+intp_to_int (int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {integer from pointer without a cast} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+void *__capability
+intp_to_voidp (int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+short *__capability
+intp_to_shortp (int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {incompatible return type} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+__intcap_t
+intcap_good (__intcap_t a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+__uintcap_t
+intcap_to_uintcap (__intcap_t a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {may change the sign} "" { xfail c++ } } */
+}
+
+int *__capability
+intcap_to_intp (__intcap_t a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {pointer from integer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+__uintcap_t
+uintcap_good (__uintcap_t a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+__intcap_t
+uintcap_to_intcap (__uintcap_t a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {may change the sign} "" { xfail c++ } } */
+}
+
+__intcap_t
+int_to_intcap (int a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-error {expects a capability type} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+const int *__capability
+cintp_good (const int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+int *__capability
+cintp_drop_c (const int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {discards 'const' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+volatile int *__capability
+vintp_good (volatile int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+int *__capability
+vintp_drop_v (volatile int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {discards 'volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+const volatile int *__capability
+cvintp_good (const volatile int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b);
+}
+
+const int *__capability
+cvintp_drop_v (const volatile int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {discards 'volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+volatile int *__capability
+cvintp_drop_c (const volatile int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {discards 'const' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+int *__capability
+cvintp_drop_cv (const volatile int *__capability a, size_t b)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-warning {discards 'const volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+int *__capability
+zero_intp (size_t b)
+{
+  return __builtin_cheri_bounds_set (0, b); /* { dg-warning {not permitted in C\+\+} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+short *__capability
+zero_shortp (size_t b)
+{
+  return __builtin_cheri_bounds_set (0, b); /* { dg-warning {not permitted in C\+\+} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+void *
+undef_zero_voidp (void)
+{
+  return __builtin_cheri_bounds_set (a, 0); /* { dg-error {'a' (undeclared|was not declared)} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+void *__capability
+zero_undef_voidp (void)
+{
+  return __builtin_cheri_bounds_set (0, b); /* { dg-error {'b' (undeclared|was not declared)} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+void *__capability
+undef_undef_voidp (void)
+{
+  return __builtin_cheri_bounds_set (a, b); /* { dg-error {'a' (undeclared|was not declared)} } */
+  /* { dg-error {'b' (undeclared|was not declared)} "" { target *-*-* } ".-1" } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
diff --git a/gcc/testsuite/c-c++-common/cheri-overloads-2.c b/gcc/testsuite/c-c++-common/cheri-overloads-2.c
new file mode 100644
index 00000000000..5bde4bab0fc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cheri-overloads-2.c
@@ -0,0 +1,80 @@
+/* { dg-require-effective-target cheri_capability_any } */
+/* { dg-options "-W -Wall -Wconversion" } */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdint.h>
+
+struct s { size_t i; };
+
+size_t
+no_args (void)
+{
+  return __builtin_cheri_length_get (); /* { dg-error {too few arguments} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+size_t
+two_args (__intcap_t x)
+{
+  return __builtin_cheri_length_get (x, 0); /* { dg-error {too many arguments} } */
+}
+
+size_t
+intp_good (int *__capability x)
+{
+  return __builtin_cheri_length_get (x);
+}
+
+size_t
+shortp_good (short *__capability x)
+{
+  return __builtin_cheri_length_get (x);
+}
+
+size_t
+int_bad (int x)
+{
+  return __builtin_cheri_length_get (x); /* { dg-error {expects a capability type} } */
+}
+
+size_t
+float_bad (float x)
+{
+  return __builtin_cheri_length_get (x); /* { dg-error {expects a capability type} } */
+}
+
+size_t
+s_bad (struct s x)
+{
+  return __builtin_cheri_length_get (x); /* { dg-error {expects a capability type} } */
+}
+
+size_t
+intcap_good (__intcap_t x)
+{
+  return __builtin_cheri_length_get (x);
+}
+
+size_t
+uintcap_good (__uintcap_t x)
+{
+  return __builtin_cheri_length_get (x);
+}
+
+size_t
+zero_good ()
+{
+  return __builtin_cheri_length_get (0);
+}
+
+int64_t
+intcap_int64 (__intcap_t x)
+{
+  return __builtin_cheri_length_get (x); /* { dg-warning {may change the sign} "" { xfail c++ } } */
+}
+
+uint32_t
+intcap_uint32 (__intcap_t x)
+{
+  return __builtin_cheri_length_get (x); /* { dg-warning {may change value} } */
+}
diff --git a/gcc/testsuite/c-c++-common/cheri-overloads-3.c b/gcc/testsuite/c-c++-common/cheri-overloads-3.c
new file mode 100644
index 00000000000..c028747de3e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cheri-overloads-3.c
@@ -0,0 +1,185 @@
+/* { dg-require-effective-target cheri_capability_any } */
+/* { dg-options "-W -Wall -Wconversion" } */
+
+#include <stddef.h>
+#include <string.h>
+
+int *__capability
+intp_shortp_good (int *__capability a, short *__capability b)
+{
+  return __builtin_cheri_seal (a, b);
+}
+
+int *__capability
+intp_intcap_good (int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b);
+}
+
+short *__capability
+shortp_shortp_good (short *__capability a, short *__capability b)
+{
+  return __builtin_cheri_seal (a, b);
+}
+
+short *__capability
+shortp_intcap_good (short *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b);
+}
+
+__intcap_t
+intcap_voidp_good (__intcap_t a, void *__capability b)
+{
+  return __builtin_cheri_seal (a, b);
+}
+
+int *__capability
+intp_size (int *__capability a, size_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-error {expects a capability type} } */
+}
+
+int *__capability
+intp_zero (int *__capability a)
+{
+  return __builtin_cheri_seal (a, 0);
+}
+
+int *__capability
+intp_float (int *__capability a, float b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-error {expects a capability type} } */
+}
+
+__intcap_t
+intp_ret_intcap (int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-warning {integer from pointer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+short *__capability
+intp_ret_shortp (int *__capability a, short *__capability b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-warning {incompatible return type} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+void *__capability
+intcap_ret_voidp (__intcap_t a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-warning {pointer from integer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+int
+int_bad (int a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-error {expects a capability type} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+float
+float_bad (float a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-error {expects a capability type} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+const int *__capability
+cintp_good (const int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b);
+}
+
+int *__capability
+cintp_drop_c (const int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-warning {discards 'const' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+volatile int *__capability
+vintp_good (volatile int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b);
+}
+
+int *__capability
+vintp_drop_v (volatile int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-warning {discards 'volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+const volatile int *__capability
+cvintp_ (const volatile int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b);
+}
+
+const int *__capability
+cvintp_drop_v (const volatile int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-warning {discards 'volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+volatile int *__capability
+cvintp_drop_c (const volatile int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-warning {discards 'const' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+int *__capability
+cvintp_drop_cv (const volatile int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-warning {discards 'const volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+int *__capability
+zero_intcap_intp (__intcap_t b)
+{
+  return __builtin_cheri_seal (0, b); /* { dg-warning {not permitted in C\+\+} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+short *__capability
+zero_intcap_shortp (__intcap_t b)
+{
+  return __builtin_cheri_seal (0, b); /* { dg-warning {not permitted in C\+\+} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+__intcap_t
+zero_zero_intcap (void)
+{
+  return __builtin_cheri_seal (0, 0); /* { dg-warning {integer from pointer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+void *__capability
+zero_zero_voidp (void)
+{
+  return __builtin_cheri_seal (0, 0);
+}
+
+void *__capability
+undef_zero_voidp (void)
+{
+  return __builtin_cheri_seal (a, 0); /* { dg-error {'a' (undeclared|was not declared)} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+void *__capability
+zero_undef_voidp (void)
+{
+  return __builtin_cheri_seal (0, b); /* { dg-error {'b' (undeclared|was not declared)} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+void *__capability
+undef_undef_voidp (void)
+{
+  return __builtin_cheri_seal (a, b); /* { dg-error {'a' (undeclared|was not declared)} } */
+  /* { dg-error {'b' (undeclared|was not declared)} "" { target *-*-* } ".-1" } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
diff --git a/gcc/testsuite/c-c++-common/cheri-overloads-4.c b/gcc/testsuite/c-c++-common/cheri-overloads-4.c
new file mode 100644
index 00000000000..6e437165f96
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cheri-overloads-4.c
@@ -0,0 +1,217 @@
+/* { dg-require-effective-target cheri_capability_any } */
+/* { dg-options "-W -Wall -Wconversion" } */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdint.h>
+
+int *__capability
+intp_good (int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+short *__capability
+shortp_good (short *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+__intcap_t
+intcap_good (__intcap_t a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+__uintcap_t
+uintcap_good (__uintcap_t a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+int *__capability
+intp_zero (int *__capability a)
+{
+  return __builtin_cheri_cap_build (a, 0);
+}
+
+int *__capability
+intp_one (int *__capability a)
+{
+  return __builtin_cheri_cap_build (a, 1);
+}
+
+int *__capability
+intp_minus_one (int *__capability a)
+{
+  return __builtin_cheri_cap_build (a, -1); /* { dg-warning {changes value} } */
+}
+
+int *__capability
+intp_float (int *__capability a, float b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {may change value} } */
+}
+
+__intcap_t
+intp_ret_intcap (int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {integer from pointer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+short *__capability
+intp_ret_shortp (int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {incompatible return type} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+void *__capability
+intcap_ret_voidp (__intcap_t a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {pointer from integer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+int *__capability
+intp_intcap (int *__capability a, __intcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {may change the sign} "" { xfail c++ } } */
+}
+
+int *__capability
+intp_intp (int *__capability a, int *__capability b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {integer from pointer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+int *__capability
+intp_int64 (int *__capability a, int64_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {may change the sign} "" { xfail c++ } } */
+}
+
+int *__capability
+intp_uint64 (int *__capability a, uint64_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+int *__capability
+intp_size (int *__capability a, size_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+int
+int_bad (int a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-error {expects a capability type} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+float
+float_bad (float a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-error {expects a capability type} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+const int *__capability
+cintp_good (const int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+int *__capability
+cintp_drop_c (const int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {discards 'const' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+volatile int *__capability
+vintp_good (volatile int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+int *__capability
+vintp_drop_v (volatile int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {discards 'volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+const volatile int *__capability
+cvintp_ (const volatile int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b);
+}
+
+const int *__capability
+cvintp_drop_v (const volatile int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {discards 'volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+volatile int *__capability
+cvintp_drop_c (const volatile int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {discards 'const' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+int *__capability
+cvintp_drop_cv (const volatile int *__capability a, __uintcap_t b)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-warning {discards 'const volatile' qualifier} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+int *__capability
+zero_uintcap_intp (__uintcap_t b)
+{
+  return __builtin_cheri_cap_build (0, b); /* { dg-warning {not permitted in C\+\+} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+short *__capability
+zero_uintcap_shortp (__uintcap_t b)
+{
+  return __builtin_cheri_cap_build (0, b); /* { dg-warning {not permitted in C\+\+} "" { target c } } */
+  /* { dg-error {invalid conversion} "" { target c++ } .-1 } */
+}
+
+__uintcap_t
+zero_zero_uintcap (void)
+{
+  return __builtin_cheri_cap_build (0, 0); /* { dg-warning {integer from pointer without a cast} "" { target c } } */
+  /* { dg-error {cannot convert} "" { target c++ } .-1 } */
+}
+
+void *__capability
+zero_zero_voidp (void)
+{
+  return __builtin_cheri_cap_build (0, 0);
+}
+
+void *__capability
+undef_zero_voidp (void)
+{
+  return __builtin_cheri_cap_build (a, 0); /* { dg-error {'a' (undeclared|was not declared)} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+void *__capability
+zero_undef_voidp (void)
+{
+  return __builtin_cheri_cap_build (0, b); /* { dg-error {'b' (undeclared|was not declared)} } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */
+
+void *__capability
+undef_undef_voidp (void)
+{
+  return __builtin_cheri_cap_build (a, b); /* { dg-error {'a' (undeclared|was not declared)} } */
+  /* { dg-error {'b' (undeclared|was not declared)} "" { target *-*-* } ".-1" } */
+} /* { dg-bogus {control reaches end} "" { xfail c } } */


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

only message in thread, other threads:[~2022-06-16 13:45 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-16 13:45 [gcc(refs/vendors/ARM/heads/morello)] Handle overloading of __builtin_cheri functions Richard Sandiford

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