public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/vendors/ARM/heads/morello)] Introduce mem{cpy, move, cmp}_c builtins
@ 2022-05-05 12:09 Matthew Malcomson
  0 siblings, 0 replies; only message in thread
From: Matthew Malcomson @ 2022-05-05 12:09 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:57f25960563da6ada6c953f6e2bb30b7db5d7844

commit 57f25960563da6ada6c953f6e2bb30b7db5d7844
Author: Matthew Malcomson <matthew.malcomson@arm.com>
Date:   Thu Mar 10 16:07:06 2022 +0000

    Introduce mem{cpy,move,cmp}_c builtins
    
    GCC uses the mem{cpy,move,cmp} library functions as a fallback for block
    memory operations that we don't want to open-code.
    
    In purecap this is just fine since the functions take capabilities and
    all pointers are capabilities.
    
    For Hybrid, these functions take non-capability pointers.  This means
    that if we decide to fall back to these library functions when asked to
    do these operations on capability pointers we would throw away the
    security behaviours on said capability pointers.
    
    To account for this, hybrid capability targets provide versions of the
    above functions which take capability pointer arguments.
    
    Strictly speaking, it is optional for these functions to be provided by
    the runtime (as documented in the "hybrid C guide", but this is the
    only reasonable fallback for a compiler and LLVM already relies on these
    functions existing rather than open-code arbitrary block memory
    operations).
    https://github.com/CTSRD_CHERI/cheri_hybrid_c_guide/
    
    This patch updates GCC to use the capability enabled versions of these
    memory operations as needs be (i.e. when we want to perform these
    operations on memory pointed to by a capability pointer).
    
    We do this by introducing three new builtins accordingly.  These
    builtins are only defined when we are targetting a hybrid capability
    target.
    
    The builtins are used automatically when one of the
    `emit_block_{copy,move,comp}_via_libcall` functions is used on a
    hybrid capability target with at least one capability-pointer argument.
    
    Notes:
     1) In using these functions we define a builtin version for each one.
        Such builtins are not in Morello LLVM.  They are added here simply
        because of the authors aesthetic tastes -- having the builtins
        defined means that the use of `memcpy` and `memcpy_c` is more
        similar both to the user and in GCC source code.  In order to avoid
        defining the builtin we could use `init_one_libfunc` and
        `emit_library_call`.
     2) In order to define these new builtins, we needed new types to use.
        The new types needed to be capabilities.  We only define this
        builtins for a hybrid capability target.  There is no existing
        mechanism to only define a type in builtin-types.def for a given
        target.  We gave the types a fallback of the non-capability version.
        Overall this means that on a non-capability target we have defined
        function types that have a name referring to capabilities but do not
        use capabilities.  Even though these functions are not used on a
        non-capability target this is slightly ugly.
        It seemed easier and less-intrusive than adding a conditional to all
        the function definition macros in each of the backends.
     3) We define new `global_trees` nodes `cap_const_ptr_type_node` and
        `cap_ptr_type_node`.  These are just added for convenience.

Diff:
---
 gcc/builtin-types.def | 10 +++++++++
 gcc/builtins.def      | 15 +++++++++++++
 gcc/expr.c            | 61 +++++++++++++++++++++++++++++++++++++++++++++------
 gcc/expr.h            | 19 +++++++++++++---
 gcc/tree-core.h       |  2 ++
 gcc/tree.c            | 35 ++++++++++++++++-------------
 gcc/tree.h            |  5 +++++
 7 files changed, 122 insertions(+), 25 deletions(-)

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index fb3fa964e87..ae321f983ec 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -111,6 +111,9 @@ DEF_PRIMITIVE_TYPE (BT_COMPLEX_DOUBLE, complex_double_type_node)
 DEF_PRIMITIVE_TYPE (BT_COMPLEX_LONGDOUBLE, complex_long_double_type_node)
 
 DEF_PRIMITIVE_TYPE (BT_PTR, ptr_type_node)
+DEF_PRIMITIVE_TYPE (BT_CAPPTR,
+		    (targetm.capability_mode ().exists ()
+		     ? cap_ptr_type_node : ptr_type_node))
 DEF_PRIMITIVE_TYPE (BT_FILEPTR, fileptr_type_node)
 DEF_PRIMITIVE_TYPE (BT_CONST_TM_PTR, const_tm_ptr_type_node)
 DEF_PRIMITIVE_TYPE (BT_FENV_T_PTR, fenv_t_ptr_type_node)
@@ -118,6 +121,9 @@ DEF_PRIMITIVE_TYPE (BT_CONST_FENV_T_PTR, const_fenv_t_ptr_type_node)
 DEF_PRIMITIVE_TYPE (BT_FEXCEPT_T_PTR, fexcept_t_ptr_type_node)
 DEF_PRIMITIVE_TYPE (BT_CONST_FEXCEPT_T_PTR, const_fexcept_t_ptr_type_node)
 DEF_PRIMITIVE_TYPE (BT_CONST_PTR, const_ptr_type_node)
+DEF_PRIMITIVE_TYPE (BT_CONST_CAPPTR,
+		    (targetm.capability_mode ().exists ()
+		     ? cap_const_ptr_type_node : const_ptr_type_node))
 DEF_PRIMITIVE_TYPE (BT_VOLATILE_PTR,
 		    build_pointer_type
 		     (build_qualified_type (void_type_node,
@@ -521,10 +527,14 @@ DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_STRING_CONST_STRING_SIZE,
 		     BT_INT, BT_CONST_STRING, BT_CONST_STRING, BT_SIZE)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_CONST_PTR_SIZE,
 		     BT_PTR, BT_PTR, BT_CONST_PTR, BT_SIZE)
+DEF_FUNCTION_TYPE_3 (BT_FN_CAPPTR_CAPPTR_CONST_CAPPTR_SIZE,
+		     BT_CAPPTR, BT_CAPPTR, BT_CONST_CAPPTR, BT_SIZE)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_CONST_PTR_SIZE,
 		     BT_VOID, BT_PTR, BT_CONST_PTR, BT_SIZE)
 DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_PTR_CONST_PTR_SIZE,
 		     BT_INT, BT_CONST_PTR, BT_CONST_PTR, BT_SIZE)
+DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_CAPPTR_CONST_CAPPTR_SIZE,
+		     BT_INT, BT_CONST_CAPPTR, BT_CONST_CAPPTR, BT_SIZE)
 DEF_FUNCTION_TYPE_3 (BT_FN_PTR_PTR_INT_SIZE,
 		     BT_PTR, BT_PTR, BT_INT, BT_SIZE)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_SIZE,
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 9b0a2235f7d..d3f0361bd9e 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -110,6 +110,18 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,	\
 	       true, true, true, ATTRS, false, true)
 
+/* A builtin corresponding to a library function that is defined specifically
+   for handling capabilities and should not be defined when
+   targetm.capability_mode does not exist.  Only define the __builtin_* version
+   and not the base name as a builtin.  Fall back to the library function if
+   the builtin is not inlined.  */
+#undef DEF_CAP_LIB_BUILTIN
+#define DEF_CAP_LIB_BUILTIN(ENUM, NAME, TYPE, ATTRS)	\
+  DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,	\
+	       false, true, false, ATTRS, true, \
+	       targetm.capability_mode ().exists () \
+	       && targetm.capability_mode ().require () != Pmode)
+
 /* A set of GCC builtins for _FloatN and _FloatNx types.  TYPE_MACRO is called
    with an argument such as FLOAT32 to produce the enum value for the type.  If
    we are compiling for the C language with GNU extensions, we enable the name
@@ -697,6 +709,9 @@ DEF_LIB_BUILTIN	       (BUILT_IN_MEMCPY, "memcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE,
 DEF_LIB_BUILTIN	       (BUILT_IN_MEMMOVE, "memmove", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMPCPY, "mempcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
 DEF_LIB_BUILTIN	       (BUILT_IN_MEMSET, "memset", BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_CAP_LIB_BUILTIN    (BUILT_IN_MEMCMP_C, "memcmp_c", BT_FN_INT_CONST_CAPPTR_CONST_CAPPTR_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
+DEF_CAP_LIB_BUILTIN    (BUILT_IN_MEMCPY_C, "memcpy_c", BT_FN_CAPPTR_CAPPTR_CONST_CAPPTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_CAP_LIB_BUILTIN    (BUILT_IN_MEMMOVE_C, "memmove_c", BT_FN_CAPPTR_CAPPTR_CONST_CAPPTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_RINDEX, "rindex", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STPCPY, "stpcpy", BT_FN_STRING_STRING_CONST_STRING, ATTR_RETNONNULL_NOTHROW_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STPNCPY, "stpncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
diff --git a/gcc/expr.c b/gcc/expr.c
index 5fe7e9781f7..9b92ddfe89f 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1988,6 +1988,8 @@ emit_block_op_via_libcall (enum built_in_function fncode, rtx dst, rtx src,
 {
   rtx dst_addr, src_addr;
   tree call_expr, dst_tree, src_tree, size_tree;
+  machine_mode src_mode = mem_address_mode (src);
+  machine_mode dst_mode = mem_address_mode (dst);
   machine_mode size_mode;
 
   /* Since dst and src are passed to a libcall, mark the corresponding
@@ -1999,13 +2001,58 @@ emit_block_op_via_libcall (enum built_in_function fncode, rtx dst, rtx src,
   if (src_expr)
     mark_addressable (src_expr);
 
-  dst_addr = copy_addr_to_reg (XEXP (dst, 0));
-  dst_addr = convert_memory_address (ptr_mode, dst_addr);
-  dst_tree = make_tree (ptr_type_node, dst_addr);
-
-  src_addr = copy_addr_to_reg (XEXP (src, 0));
-  src_addr = convert_memory_address (ptr_mode, src_addr);
-  src_tree = make_tree (ptr_type_node, src_addr);
+  scalar_addr_mode addr_mode = Pmode;
+  scalar_addr_mode pointer_mode = ptr_mode;
+  tree pointer_type_node = ptr_type_node;
+  rtx dst_tmp = XEXP (dst, 0);
+  rtx src_tmp = XEXP (src, 0);
+
+  if (fncode == BUILT_IN_MEMCPY_C
+      || fncode == BUILT_IN_MEMCMP_C
+      || fncode == BUILT_IN_MEMMOVE_C)
+    {
+      /* We are in hybrid capability mode, and at least one of the pointers to
+	 this memory call is a `__capability` pointer.  This means that we can
+	 not use the standard library versions, but must use the `_c` versions
+	 instead.  Those `_c` versions will use the capability metadata given
+	 in the `__capability` pointer, which means that the permissions and
+	 bounds on that capability will be respected.
+
+	 If there is only one __capability pointer then we need to make a
+	 capability from the non-capability pointer to match the library
+	 function interface.  This should be made from the general data
+	 capability as that is the way that said non-capability pointer will be
+	 accessed.  */
+      addr_mode = targetm.addr_space.address_mode
+			    (addr_space_t (ADDR_SPACE_GENERIC), true);
+      pointer_mode = targetm.addr_space.pointer_mode
+			    (addr_space_t (ADDR_SPACE_GENERIC), true);
+      pointer_type_node = cap_ptr_type_node;
+
+      rtx tmp_ddc = gen_reg_rtx (addr_mode);
+      targetm.gen_cap_global_data_get (tmp_ddc);
+
+      /* Only use these functions when we need to handle capabilities and those
+	 capabilities are not the standard capability type.  */
+      gcc_assert (src_mode == pointer_mode || dst_mode == pointer_mode);
+      gcc_assert (targetm.capability_mode ().exists ()
+		  && targetm.capability_mode ().require () != Pmode);
+
+      if (! CAPABILITY_MODE_P (src_mode))
+	src_tmp = simplify_gen_binary (REPLACE_ADDRESS_VALUE,
+				       addr_mode, tmp_ddc, src_tmp);
+      if (! CAPABILITY_MODE_P (dst_mode))
+	dst_tmp = simplify_gen_binary (REPLACE_ADDRESS_VALUE,
+				       addr_mode, tmp_ddc, dst_tmp);
+    }
+
+  dst_addr = copy_to_mode_reg (addr_mode, dst_tmp);
+  dst_addr = convert_memory_address (pointer_mode, dst_addr);
+  dst_tree = make_tree (pointer_type_node, dst_addr);
+
+  src_addr = copy_to_mode_reg (addr_mode, src_tmp);
+  src_addr = convert_memory_address (pointer_mode, src_addr);
+  src_tree = make_tree (pointer_type_node, src_addr);
 
   size_mode = TYPE_MODE (sizetype);
   size = convert_to_mode (size_mode, size, 1);
diff --git a/gcc/expr.h b/gcc/expr.h
index 8eac75957e0..35d1e1fc7ac 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -75,23 +75,36 @@ extern rtx convert_modes (machine_mode, machine_mode, rtx, int);
 extern rtx emit_block_op_via_libcall (enum built_in_function, rtx, rtx, rtx,
 				      bool);
 
+/* The _C versions of memcpy, memcpy, and memmove are only defined when they
+   should be preferred over the non-_C versions if at least one of the
+   arguments is a capability.  This is essentially when we are compiling for a
+   hybrid capability target. */
+#define MAYBE_CAP_VERSION(fncode) \
+  (builtin_decl_explicit_p (fncode##_C) \
+   && (CAPABILITY_MODE_P (GET_MODE (XEXP (src, 0))) \
+       || CAPABILITY_MODE_P (GET_MODE (XEXP (dst, 0))))) \
+    ? fncode##_C : fncode
 static inline rtx
 emit_block_copy_via_libcall (rtx dst, rtx src, rtx size, bool tailcall = false)
 {
-  return emit_block_op_via_libcall (BUILT_IN_MEMCPY, dst, src, size, tailcall);
+  return emit_block_op_via_libcall (MAYBE_CAP_VERSION (BUILT_IN_MEMCPY),
+				    dst, src, size, tailcall);
 }
 
 static inline rtx
 emit_block_move_via_libcall (rtx dst, rtx src, rtx size, bool tailcall = false)
 {
-  return emit_block_op_via_libcall (BUILT_IN_MEMMOVE, dst, src, size, tailcall);
+  return emit_block_op_via_libcall (MAYBE_CAP_VERSION (BUILT_IN_MEMMOVE),
+				    dst, src, size, tailcall);
 }
 
 static inline rtx
 emit_block_comp_via_libcall (rtx dst, rtx src, rtx size, bool tailcall = false)
 {
-  return emit_block_op_via_libcall (BUILT_IN_MEMCMP, dst, src, size, tailcall);
+  return emit_block_op_via_libcall (MAYBE_CAP_VERSION (BUILT_IN_MEMCMP),
+				    dst, src, size, tailcall);
 }
+#undef MAYBE_CAP_VERSION
 
 /* Emit code to move a block Y to a block X.  */
 enum block_op_methods
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index c738a118ba2..9ea47809add 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -682,6 +682,8 @@ enum tree_index {
   TI_CONST_PTR_TYPE,
   TI_INTCAP_TYPE,
   TI_UINTCAP_TYPE,
+  TI_CAP_PTR_TYPE,
+  TI_CAP_CONST_PTR_TYPE,
   TI_SIZE_TYPE,
   TI_PID_TYPE,
   TI_PTRDIFF_TYPE,
diff --git a/gcc/tree.c b/gcc/tree.c
index c7291a67aac..da8d55a2905 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -10478,21 +10478,6 @@ build_common_tree_nodes (bool signed_char)
 	gcc_unreachable ();
     }
 
-  opt_scalar_addr_mode opt_cap_mode = targetm.capability_mode();
-  if (opt_cap_mode.exists())
-    {
-      scalar_addr_mode cap_mode = opt_cap_mode.require();
-      /* `build_intcap_type_for_mode` caches its return values in
-	 `uintcap_type_node` and `intcap_type_node`, so we don't need to do the
-	 assignment here.  We *do* need to ensure that those nodes are set by
-	 the time this function returns, since they are used by other code
-	 without calling `build_intcap_type_for_mode`.  */
-      gcc_assert (build_intcap_type_for_mode (cap_mode, 0)
-		  == intcap_type_node);
-      gcc_assert (build_intcap_type_for_mode (cap_mode, 1)
-		  == uintcap_type_node);
-    }
-
   /* Fill in the rest of the sized types.  Reuse existing type nodes
      when possible.  */
   intQI_type_node = make_or_reuse_type (GET_MODE_BITSIZE (QImode), 0);
@@ -10567,6 +10552,26 @@ build_common_tree_nodes (bool signed_char)
 
   pointer_sized_int_node = build_nonstandard_integer_type (POINTER_SIZE, 1);
 
+  opt_scalar_addr_mode opt_cap_mode = targetm.capability_mode();
+  if (opt_cap_mode.exists())
+    {
+      scalar_addr_mode cap_mode = opt_cap_mode.require();
+      /* `build_intcap_type_for_mode` caches its return values in
+	 `uintcap_type_node` and `intcap_type_node`, so we don't need to do the
+	 assignment here.  We *do* need to ensure that those nodes are set by
+	 the time this function returns, since they are used by other code
+	 without calling `build_intcap_type_for_mode`.  */
+      gcc_assert (build_intcap_type_for_mode (cap_mode, 0)
+		  == 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_const_ptr_type_node
+	= build_pointer_type_for_mode
+	      (build_type_variant (void_type_node, 1, 0), cap_mode, false);
+    }
+
   float_type_node = make_node (REAL_TYPE);
   TYPE_PRECISION (float_type_node) = FLOAT_TYPE_SIZE;
   layout_type (float_type_node);
diff --git a/gcc/tree.h b/gcc/tree.h
index 7c99f4f6e96..2bc986e0869 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4201,6 +4201,11 @@ tree_strip_any_location_wrapper (tree exp)
 /* Capability integer types.  */
 #define intcap_type_node		global_trees[TI_INTCAP_TYPE]
 #define uintcap_type_node		global_trees[TI_UINTCAP_TYPE]
+/* Capability pointer type.  Only different to ptr_type_node in Hybrid.  */
+#define cap_ptr_type_node		global_trees[TI_CAP_PTR_TYPE]
+/* Capability const pointer type.  Different from const_ptr_type_node in Hybrid
+   by having a capability mode.  */
+#define cap_const_ptr_type_node		global_trees[TI_CAP_CONST_PTR_TYPE]
 /* The C type `size_t'.  */
 #define size_type_node                  global_trees[TI_SIZE_TYPE]
 #define pid_type_node                   global_trees[TI_PID_TYPE]


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

only message in thread, other threads:[~2022-05-05 12:09 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-05 12:09 [gcc(refs/vendors/ARM/heads/morello)] Introduce mem{cpy, move, cmp}_c builtins Matthew Malcomson

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