Hi,

Arm Memory Tagging Extension (MTE) is published with Armv8.5-A.
It can be used for spatial and temporal memory safety detection and 
lightweight lock and key system.

This patch enables new intrinsics leveraging MTE instructions to 
implement functionalities of creating tags, setting tags, reading tags, 
and manipulating tags.
The intrinsics are part of Arm ACLE extension: 
https://developer.arm.com/docs/101028/latest/memory-tagging-intrinsics
The MTE ISA specification can be found at 
https://developer.arm.com/docs/ddi0487/latest chapter D6.

Bootstraped and regtested for aarch64-none-linux-gnu.

Please help to check if it's OK for trunk.

Many Thanks
Dennis

gcc/ChangeLog:

2019-10-16  Dennis Zhang  <dennis.zh...@arm.com>

        * config/aarch64/aarch64-builtins.c (enum aarch64_builtins): Add
        AARCH64_MEMTAG_BUILTIN_START, AARCH64_MEMTAG_BUILTIN_IRG,
        AARCH64_MEMTAG_BUILTIN_GMI, AARCH64_MEMTAG_BUILTIN_SUBP,
        AARCH64_MEMTAG_BUILTIN_INC_TAG, AARCH64_MEMTAG_BUILTIN_SET_TAG,
        AARCH64_MEMTAG_BUILTIN_GET_TAG, and AARCH64_MEMTAG_BUILTIN_END.
        (aarch64_init_memtag_builtins): New.
        (AARCH64_INIT_MEMTAG_BUILTINS_DECL): New macro.
        (aarch64_general_init_builtins): Call aarch64_init_memtag_builtins.
        (aarch64_expand_builtin_memtag): New.
        (aarch64_general_expand_builtin): Call aarch64_expand_builtin_memtag.
        (AARCH64_BUILTIN_SUBCODE): New macro.
        (aarch64_resolve_overloaded_memtag): New.
        (aarch64_resolve_overloaded_builtin): New hook. Call
        aarch64_resolve_overloaded_memtag to handle overloaded MTE builtins.
        * config/aarch64/aarch64-c.c (aarch64_update_cpp_builtins): Define
        __ARM_FEATURE_MEMORY_TAGGING when enabled.
        * config/aarch64/aarch64-protos.h (aarch64_resolve_overloaded_builtin):
        Add declaration.
        * config/aarch64/aarch64.c (TARGET_RESOLVE_OVERLOADED_BUILTIN):
        New hook.
        * config/aarch64/aarch64.h (AARCH64_ISA_MEMTAG): New macro.
        (TARGET_MEMTAG): Likewise.
        * config/aarch64/aarch64.md (define_c_enum "unspec"): Add
        UNSPEC_GEN_TAG, UNSPEC_GEN_TAG_RND, and UNSPEC_TAG_SPACE.
        (irg, gmi, subp, addg, ldg, stg): New instructions.
        * config/aarch64/arm_acle.h (__arm_mte_create_random_tag): New macro.
        (__arm_mte_exclude_tag, __arm_mte_increment_tag): Likewise.
        (__arm_mte_ptrdiff, __arm_mte_set_tag, __arm_mte_get_tag): Likewise.
        * config/aarch64/predicates.md (aarch64_memtag_tag_offset): New.
        (aarch64_granule16_uimm6, aarch64_granule16_simm9): New.
        * config/arm/types.md (memtag): New.
        * doc/invoke.texi (-memtag): Update description.

gcc/testsuite/ChangeLog:

2019-10-16  Dennis Zhang  <dennis.zh...@arm.com>

        * gcc.target/aarch64/acle/memtag_1.c: New test.
        * gcc.target/aarch64/acle/memtag_2.c: New test.
        * gcc.target/aarch64/acle/memtag_3.c: New test.
diff --git a/gcc/config/aarch64/aarch64-builtins.c b/gcc/config/aarch64/aarch64-builtins.c
index e02ece8672a..b77bcc42eab 100644
--- a/gcc/config/aarch64/aarch64-builtins.c
+++ b/gcc/config/aarch64/aarch64-builtins.c
@@ -445,6 +445,15 @@ enum aarch64_builtins
   AARCH64_TME_BUILTIN_TCOMMIT,
   AARCH64_TME_BUILTIN_TTEST,
   AARCH64_TME_BUILTIN_TCANCEL,
+  /* MEMTAG builtins.  */
+  AARCH64_MEMTAG_BUILTIN_START,
+  AARCH64_MEMTAG_BUILTIN_IRG,
+  AARCH64_MEMTAG_BUILTIN_GMI,
+  AARCH64_MEMTAG_BUILTIN_SUBP,
+  AARCH64_MEMTAG_BUILTIN_INC_TAG,
+  AARCH64_MEMTAG_BUILTIN_SET_TAG,
+  AARCH64_MEMTAG_BUILTIN_GET_TAG,
+  AARCH64_MEMTAG_BUILTIN_END,
   AARCH64_BUILTIN_MAX
 };
 
@@ -1111,6 +1120,52 @@ aarch64_init_tme_builtins (void)
 				   AARCH64_TME_BUILTIN_TCANCEL);
 }
 
+/* Initialize the memory tagging extension (MTE) builtins.  */
+struct
+{
+  tree ftype;
+  enum insn_code icode;
+} aarch64_memtag_builtin_data[AARCH64_MEMTAG_BUILTIN_END -
+			      AARCH64_MEMTAG_BUILTIN_START - 1];
+
+static void
+aarch64_init_memtag_builtins (void)
+{
+  tree fntype = NULL;
+
+#define AARCH64_INIT_MEMTAG_BUILTINS_DECL(F, N, I, T) \
+  aarch64_builtin_decls[AARCH64_MEMTAG_BUILTIN_##F] \
+    = aarch64_general_add_builtin ("__builtin_aarch64_memtag_"#N, \
+				   T, AARCH64_MEMTAG_BUILTIN_##F); \
+  aarch64_memtag_builtin_data[AARCH64_MEMTAG_BUILTIN_##F - \
+			      AARCH64_MEMTAG_BUILTIN_START - 1] = \
+				{T, CODE_FOR_##I};
+
+  fntype = build_function_type_list (ptr_type_node, ptr_type_node,
+				     uint64_type_node, NULL);
+  AARCH64_INIT_MEMTAG_BUILTINS_DECL (IRG, irg, irg, fntype);
+
+  fntype = build_function_type_list (uint64_type_node, ptr_type_node,
+				     uint64_type_node, NULL);
+  AARCH64_INIT_MEMTAG_BUILTINS_DECL (GMI, gmi, gmi, fntype);
+
+  fntype = build_function_type_list (ptrdiff_type_node, ptr_type_node,
+				     ptr_type_node, NULL);
+  AARCH64_INIT_MEMTAG_BUILTINS_DECL (SUBP, subp, subp, fntype);
+
+  fntype = build_function_type_list (ptr_type_node, ptr_type_node,
+				     unsigned_type_node, NULL);
+  AARCH64_INIT_MEMTAG_BUILTINS_DECL (INC_TAG, inc_tag, addg, fntype);
+
+  fntype = build_function_type_list (void_type_node, ptr_type_node, NULL);
+  AARCH64_INIT_MEMTAG_BUILTINS_DECL (SET_TAG, set_tag, stg, fntype);
+
+  fntype = build_function_type_list (ptr_type_node, ptr_type_node, NULL);
+  AARCH64_INIT_MEMTAG_BUILTINS_DECL (GET_TAG, get_tag, ldg, fntype);
+
+#undef AARCH64_INIT_MEMTAG_BUILTINS_DECL
+}
+
 /* Initialize all builtins in the AARCH64_BUILTIN_GENERAL group.  */
 void
 aarch64_general_init_builtins (void)
@@ -1162,6 +1217,9 @@ aarch64_general_init_builtins (void)
 
   if (TARGET_TME)
     aarch64_init_tme_builtins ();
+
+  if (TARGET_MEMTAG)
+    aarch64_init_memtag_builtins ();
 }
 
 /* Implement TARGET_BUILTIN_DECL for the AARCH64_BUILTIN_GENERAL group.  */
@@ -1607,6 +1665,73 @@ aarch64_expand_builtin_tme (int fcode, tree exp, rtx target)
     return target;
 }
 
+/* Expand an expression EXP that calls a MEMTAG built-in FCODE
+   with result going to TARGET.  */
+static rtx
+aarch64_expand_builtin_memtag (int fcode, tree exp, rtx target)
+{
+  rtx pat = NULL;
+  enum insn_code icode = aarch64_memtag_builtin_data[fcode -
+			   AARCH64_MEMTAG_BUILTIN_START - 1].icode;
+
+  rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
+  machine_mode mode0 = GET_MODE (op0);
+  op0 = force_reg (mode0 == VOIDmode ? DImode : mode0, op0);
+  op0 = convert_to_mode (DImode, op0, true);
+
+  switch (fcode)
+    {
+      case AARCH64_MEMTAG_BUILTIN_IRG:
+      case AARCH64_MEMTAG_BUILTIN_GMI:
+      case AARCH64_MEMTAG_BUILTIN_SUBP:
+      case AARCH64_MEMTAG_BUILTIN_INC_TAG:
+	{
+	  if (! target
+	      || GET_MODE (target) != DImode
+	      || ! (*insn_data[icode].operand[0].predicate) (target, DImode))
+	    target = gen_reg_rtx (DImode);
+
+	  if (fcode == AARCH64_MEMTAG_BUILTIN_INC_TAG)
+	    {
+	      rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
+
+	      if ((*insn_data[icode].operand[3].predicate) (op1, QImode))
+		{
+		  pat = GEN_FCN (icode) (target, op0, const0_rtx, op1);
+		  break;
+		}
+	      error ("%Kargument %d must be a constant immediate "
+		     "in range [0,15]", exp, 2);
+	      return target;
+	    }
+	  else
+	    {
+	      rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
+	      machine_mode mode1 = GET_MODE (op1);
+	      op1 = force_reg (mode1 == VOIDmode ? DImode : mode1, op1);
+	      op1 = convert_to_mode (DImode, op1, true);
+	      pat = GEN_FCN (icode) (target, op0, op1);
+	    }
+	  break;
+	}
+      case AARCH64_MEMTAG_BUILTIN_GET_TAG:
+	target = op0;
+	pat = GEN_FCN (icode) (target, op0, const0_rtx);
+	break;
+      case AARCH64_MEMTAG_BUILTIN_SET_TAG:
+	pat = GEN_FCN (icode) (op0, op0, const0_rtx);
+	break;
+      default:
+	gcc_unreachable();
+    }
+
+  if (!pat)
+    return NULL_RTX;
+
+  emit_insn (pat);
+  return target;
+}
+
 /* Expand an expression EXP that calls built-in function FCODE,
    with result going to TARGET if that's convenient.  */
 rtx
@@ -1737,6 +1862,10 @@ aarch64_general_expand_builtin (unsigned int fcode, tree exp, rtx target)
       || fcode == AARCH64_TME_BUILTIN_TCANCEL)
     return aarch64_expand_builtin_tme (fcode, exp, target);
 
+  if (fcode >= AARCH64_MEMTAG_BUILTIN_START
+      && fcode <= AARCH64_MEMTAG_BUILTIN_END)
+    return aarch64_expand_builtin_memtag (fcode, exp, target);
+
   gcc_unreachable ();
 }
 
@@ -2111,6 +2240,106 @@ aarch64_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
 			    reload_fenv, restore_fnenv), update_call);
 }
 
+/* Resolve overloaded MEMTAG build-in functions.  */
+#define AARCH64_BUILTIN_SUBCODE(F) \
+  (DECL_MD_FUNCTION_CODE (F) >> AARCH64_BUILTIN_SHIFT)
+
+static tree
+aarch64_resolve_overloaded_memtag (location_t loc,
+				   tree fndecl, void *pass_params)
+{
+  vec<tree, va_gc> *params = static_cast<vec<tree, va_gc> *> (pass_params);
+  unsigned param_num = params ? params->length() : 0;
+  unsigned int fcode = AARCH64_BUILTIN_SUBCODE (fndecl);
+  tree inittype = aarch64_memtag_builtin_data[
+		    fcode - AARCH64_MEMTAG_BUILTIN_START - 1].ftype;
+  unsigned arg_num = list_length (TYPE_ARG_TYPES (inittype)) - 1;
+
+  if (param_num != arg_num)
+    {
+      TREE_TYPE (fndecl) = inittype;
+      return NULL_TREE;
+    }
+  tree retype = NULL;
+
+  if (fcode == AARCH64_MEMTAG_BUILTIN_SUBP)
+    {
+      tree t0 = TREE_TYPE ((*params)[0]);
+      tree t1 = TREE_TYPE ((*params)[1]);
+
+      if (t0 == error_mark_node || TREE_CODE (t0) != POINTER_TYPE)
+	t0 = ptr_type_node;
+      if (t1 == error_mark_node || TREE_CODE (t1) != POINTER_TYPE)
+	t1 = ptr_type_node;
+
+      if (TYPE_MODE (t0) != DImode)
+	warning_at (loc, 1, "expected 64-bit address but argument 1 is %d-bit",
+	    (int)tree_to_shwi (DECL_SIZE ((*params)[0])));
+
+      if (TYPE_MODE (t1) != DImode)
+	warning_at (loc, 1, "expected 64-bit address but argument 2 is %d-bit",
+	    (int)tree_to_shwi (DECL_SIZE ((*params)[1])));
+
+      retype = build_function_type_list (ptrdiff_type_node, t0, t1, NULL);
+    }
+  else
+    {
+      tree t0 = TREE_TYPE ((*params)[0]);
+
+      if (t0 == error_mark_node || TREE_CODE (t0) != POINTER_TYPE)
+	{
+	  TREE_TYPE (fndecl) = inittype;
+	  return NULL_TREE;
+	}
+
+      if (TYPE_MODE (t0) != DImode)
+	warning_at (loc, 1, "expected 64-bit address but argument 1 is %d-bit",
+	    (int)tree_to_shwi (DECL_SIZE ((*params)[0])));
+
+      switch (fcode)
+	{
+	case AARCH64_MEMTAG_BUILTIN_IRG:
+	  retype = build_function_type_list (t0, t0, uint64_type_node, NULL);
+	  break;
+	case AARCH64_MEMTAG_BUILTIN_GMI:
+	  retype = build_function_type_list (uint64_type_node, t0,
+	      uint64_type_node, NULL);
+	  break;
+	case AARCH64_MEMTAG_BUILTIN_INC_TAG:
+	  retype = build_function_type_list (t0, t0, unsigned_type_node, NULL);
+	  break;
+	case AARCH64_MEMTAG_BUILTIN_SET_TAG:
+	  retype = build_function_type_list (void_type_node, t0, NULL);
+	  break;
+	case AARCH64_MEMTAG_BUILTIN_GET_TAG:
+	  retype = build_function_type_list (t0, t0, NULL);
+	  break;
+	default:
+	  return NULL_TREE;
+	}
+    }
+
+  if (!retype || retype == error_mark_node)
+    TREE_TYPE (fndecl) = inittype;
+  else
+    TREE_TYPE (fndecl) = retype;
+
+  return NULL_TREE;
+}
+
+/* Implement the TARGET_RESOLVE_OVERLOADED_BUILTIN hook.  */
+tree
+aarch64_resolve_overloaded_builtin (location_t loc, tree function,
+				    void *pass_params)
+{
+  unsigned int fcode = AARCH64_BUILTIN_SUBCODE (function);
+
+  if (fcode >= AARCH64_MEMTAG_BUILTIN_START
+      && fcode <= AARCH64_MEMTAG_BUILTIN_END)
+    return aarch64_resolve_overloaded_memtag(loc, function, pass_params);
+
+  return NULL_TREE;
+}
 
 #undef AARCH64_CHECK_BUILTIN_MODE
 #undef AARCH64_FIND_FRINT_VARIANT
diff --git a/gcc/config/aarch64/aarch64-c.c b/gcc/config/aarch64/aarch64-c.c
index 137aa18af46..fe48e786214 100644
--- a/gcc/config/aarch64/aarch64-c.c
+++ b/gcc/config/aarch64/aarch64-c.c
@@ -160,6 +160,7 @@ aarch64_update_cpp_builtins (cpp_reader *pfile)
 
   aarch64_def_or_undef (TARGET_FRINT, "__ARM_FEATURE_FRINT", pfile);
   aarch64_def_or_undef (TARGET_TME, "__ARM_FEATURE_TME", pfile);
+  aarch64_def_or_undef (TARGET_MEMTAG, "__ARM_FEATURE_MEMORY_TAGGING", pfile);
 
   /* Not for ACLE, but required to keep "float.h" correct if we switch
      target between implementations that do or do not support ARMv8.2-A
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index ab27a1263f5..688317d6549 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -720,4 +720,6 @@ extern const atomic_ool_names aarch64_ool_ldset_names;
 extern const atomic_ool_names aarch64_ool_ldclr_names;
 extern const atomic_ool_names aarch64_ool_ldeor_names;
 
+tree aarch64_resolve_overloaded_builtin (location_t, tree, void *);
+
 #endif /* GCC_AARCH64_PROTOS_H */
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 1f0e74ac1a4..67eab2f0f37 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -21087,6 +21087,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_ASM_POST_CFI_STARTPROC
 #define TARGET_ASM_POST_CFI_STARTPROC aarch64_post_cfi_startproc
 
+#undef TARGET_RESOLVE_OVERLOADED_BUILTIN
+#define TARGET_RESOLVE_OVERLOADED_BUILTIN aarch64_resolve_overloaded_builtin
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-aarch64.h"
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index abd14a2f92c..a7a0e39787a 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -247,6 +247,7 @@ extern unsigned aarch64_architecture_version;
 #define AARCH64_ISA_RCPC8_4	   (aarch64_isa_flags & AARCH64_FL_RCPC8_4)
 #define AARCH64_ISA_V8_5	   (aarch64_isa_flags & AARCH64_FL_V8_5)
 #define AARCH64_ISA_TME		   (aarch64_isa_flags & AARCH64_FL_TME)
+#define AARCH64_ISA_MEMTAG	   (aarch64_isa_flags & AARCH64_FL_MEMTAG)
 
 /* Crypto is an optional extension to AdvSIMD.  */
 #define TARGET_CRYPTO (TARGET_SIMD && AARCH64_ISA_CRYPTO)
@@ -300,6 +301,9 @@ extern unsigned aarch64_architecture_version;
 /* TME instructions are enabled.  */
 #define TARGET_TME (AARCH64_ISA_TME)
 
+/* Memory Tagging instructions optional to Armv8.5 enabled through +memtag.  */
+#define TARGET_MEMTAG (AARCH64_ISA_V8_5 && AARCH64_ISA_MEMTAG)
+
 /* Make sure this is always defined so we don't have to check for ifdefs
    but rather use normal ifs.  */
 #ifndef TARGET_FIX_ERR_A53_835769_DEFAULT
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index e7a6930465e..7b6ec3e6bcb 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -244,6 +244,9 @@
     UNSPEC_SPECULATION_TRACKER
     UNSPEC_COPYSIGN
     UNSPEC_TTEST		; Represent transaction test.
+    UNSPEC_GEN_TAG		; Generate a 4-bit MTE tag.
+    UNSPEC_GEN_TAG_RND		; Generate a random 4-bit MTE tag.
+    UNSPEC_TAG_SPACE		; Translate address to MTE tag address space.
 ])
 
 (define_c_enum "unspecv" [
@@ -7338,6 +7341,93 @@
   [(set_attr "type" "tme")]
 )
 
+;; Memory Tagging Extension (MTE) instructions.
+
+(define_insn "irg"
+  [(set (match_operand:DI 0 "register_operand" "=rk")
+	(ior:DI
+	 (and:DI (match_operand:DI 1 "register_operand" "rk")
+		 (const_int -1080863910568919041)) ;; 0xf0ff...
+	 (ashift:DI (unspec:QI [(match_operand:DI 2 "register_operand" "r")]
+		     UNSPEC_GEN_TAG_RND)
+		    (const_int 56))))]
+  "TARGET_MEMTAG"
+  "irg\\t%0, %1, %2"
+  [(set_attr "type" "memtag")]
+)
+
+(define_insn "gmi"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(ior:DI (ashift:DI
+		 (const_int 1)
+		 (and:QI (lshiftrt:DI
+			  (match_operand:DI 1 "register_operand" "rk")
+			  (const_int 56)) (const_int 15)))
+		(match_operand:DI 2 "register_operand" "r")))]
+  "TARGET_MEMTAG"
+  "gmi\\t%0, %1, %2"
+  [(set_attr "type" "memtag")]
+)
+
+(define_insn "addg"
+  [(set (match_operand:DI 0 "register_operand" "=rk")
+	(ior:DI
+	 (and:DI (plus:DI (match_operand:DI 1 "register_operand" "rk")
+			  (match_operand:DI 2 "aarch64_granule16_uimm6" "i"))
+		 (const_int -1080863910568919041)) ;; 0xf0ff...
+	 (ashift:DI
+	  (unspec:QI
+	   [(and:QI (lshiftrt:DI (match_dup 1) (const_int 56)) (const_int 15))
+	    (match_operand:QI 3 "aarch64_memtag_tag_offset" "i")]
+	   UNSPEC_GEN_TAG)
+	  (const_int 56))))]
+  "TARGET_MEMTAG"
+  "addg\\t%0, %1, #%2, #%3"
+  [(set_attr "type" "memtag")]
+)
+
+(define_insn "subp"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(minus:DI
+	  (and:DI (match_operand:DI 1 "register_operand" "rk")
+		  (const_int 72057594037927935)) ;; 0x00ff...
+	  (and:DI (match_operand:DI 2 "register_operand" "rk")
+		  (const_int 72057594037927935))))] ;; 0x00ff...
+  "TARGET_MEMTAG"
+  "subp\\t%0, %1, %2"
+  [(set_attr "type" "memtag")]
+)
+
+;; LDG will use the 16-byte aligned value of the address.
+(define_insn "ldg"
+  [(set (match_operand:DI 0 "register_operand" "+r")
+	(ior:DI
+	 (and:DI (match_dup 0) (const_int -1080863910568919041)) ;; 0xf0ff...
+	 (ashift:DI
+	  (mem:QI (unspec:DI
+	   [(and:DI (plus:DI (match_operand:DI 1 "register_operand" "rk")
+			     (match_operand:DI 2 "aarch64_granule16_simm9" "i"))
+		    (const_int -16))] UNSPEC_TAG_SPACE))
+	  (const_int 56))))]
+  "TARGET_MEMTAG"
+  "ldg\\t%0, [%1, #%2]"
+  [(set_attr "type" "memtag")]
+)
+
+;; STG doesn't align the address but aborts with alignment fault
+;; when the address is not 16-byte aligned.
+(define_insn "stg"
+  [(set (mem:QI (unspec:DI
+	 [(plus:DI (match_operand:DI 1 "register_operand" "rk")
+		   (match_operand:DI 2 "aarch64_granule16_simm9" "i"))]
+	 UNSPEC_TAG_SPACE))
+	(and:QI (lshiftrt:DI (match_operand:DI 0 "register_operand" "rk")
+			     (const_int 56)) (const_int 15)))]
+  "TARGET_MEMTAG"
+  "stg\\t%0, [%1, #%2]"
+  [(set_attr "type" "memtag")]
+)
+
 ;; AdvSIMD Stuff
 (include "aarch64-simd.md")
 
diff --git a/gcc/config/aarch64/arm_acle.h b/gcc/config/aarch64/arm_acle.h
index 147dfe0585c..097ed956e3e 100644
--- a/gcc/config/aarch64/arm_acle.h
+++ b/gcc/config/aarch64/arm_acle.h
@@ -193,6 +193,29 @@ __ttest (void)
 #pragma GCC pop_options
 #endif
 
+#pragma GCC push_options
+#pragma GCC target ("arch=armv8.5-a+memtag")
+
+#define __arm_mte_create_random_tag(__ptr, __u64_mask) \
+  __builtin_aarch64_memtag_irg(__ptr, __u64_mask)
+
+#define __arm_mte_exclude_tag(__ptr, __u64_excluded) \
+  __builtin_aarch64_memtag_gmi(__ptr, __u64_excluded)
+
+#define __arm_mte_ptrdiff(__ptr_a, __ptr_b) \
+  __builtin_aarch64_memtag_subp(__ptr_a, __ptr_b)
+
+#define __arm_mte_increment_tag(__ptr, __u_offset) \
+  __builtin_aarch64_memtag_inc_tag(__ptr, __u_offset)
+
+#define __arm_mte_set_tag(__tagged_address) \
+  __builtin_aarch64_memtag_set_tag(__tagged_address)
+
+#define __arm_mte_get_tag(__address) \
+  __builtin_aarch64_memtag_get_tag(__address)
+
+#pragma GCC pop_options
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md
index d8c377994d6..a5866944195 100644
--- a/gcc/config/aarch64/predicates.md
+++ b/gcc/config/aarch64/predicates.md
@@ -786,3 +786,17 @@
 
 (define_predicate "aarch64_sve_any_binary_operator"
   (match_code "plus,minus,mult,div,udiv,smax,umax,smin,umin,and,ior,xor"))
+
+(define_predicate "aarch64_memtag_tag_offset"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 15)")))
+
+(define_predicate "aarch64_granule16_uimm6"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 1008)
+		    && !(INTVAL (op) & 0xf)")))
+
+(define_predicate "aarch64_granule16_simm9"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op),  -4096, 4080)
+		    && !(INTVAL (op) & 0xf)")))
diff --git a/gcc/config/arm/types.md b/gcc/config/arm/types.md
index 60faad65979..df39522f2ad 100644
--- a/gcc/config/arm/types.md
+++ b/gcc/config/arm/types.md
@@ -1096,7 +1096,8 @@
   crypto_sm3,\
   crypto_sm4,\
   coproc,\
-  tme"
+  tme,\
+  memtag"
    (const_string "untyped"))
 
 ; Is this an (integer side) multiply with a 32-bit (or smaller) result?
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1407d019d14..3601b63de5e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -16206,9 +16206,8 @@ Enable the Armv8.5-a Random Number instructions.  This option is only to
 enable the extension at the assembler level and does not affect code
 generation.
 @item memtag
-Enable the Armv8.5-a Memory Tagging Extensions.  This option is only to
-enable the extension at the assembler level and does not affect code
-generation.
+Enable the Armv8.5-a Memory Tagging Extensions.
+Use of this option with architectures prior to Armv8.5-A is not supported.
 @item sb
 Enable the Armv8-a Speculation Barrier instruction.  This option is only to
 enable the extension at the assembler level and does not affect code
diff --git a/gcc/testsuite/gcc.target/aarch64/acle/memtag_1.c b/gcc/testsuite/gcc.target/aarch64/acle/memtag_1.c
new file mode 100644
index 00000000000..a738f7e0b58
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/acle/memtag_1.c
@@ -0,0 +1,61 @@
+/* Test the MEMTAG ACLE intrinsic.  */
+
+/* { dg-do compile { target aarch64-*-* } } */
+/* { dg-options "-O2 -march=armv8.5-a+memtag" } */
+
+#include "arm_acle.h"
+
+/* irg */
+
+void *
+test_memtag_1 (void *p)
+{
+  return __arm_mte_create_random_tag (p, 0);
+}
+
+/* gmi */
+
+uint64_t
+test_memtag_2 (void *p)
+{
+  return __arm_mte_exclude_tag (p, 0);
+}
+
+/* addg */
+
+void *
+test_memtag_3 (void *p)
+{
+  return __arm_mte_increment_tag (p, 1);
+}
+
+/* subp */
+
+int64_t
+test_memtag_4 (void *p, void *q)
+{
+  return __arm_mte_ptrdiff (p, q);
+}
+
+/* ldg */
+
+void *
+test_memtag_5 (void *p)
+{
+  return __arm_mte_get_tag (p);
+}
+
+/* stg */
+
+void
+test_memtag_6 (void *p)
+{
+  __arm_mte_set_tag (p);
+}
+
+/* { dg-final { scan-assembler-times {irg\tx..?, x..?, x..?\n} 1 } } */
+/* { dg-final { scan-assembler-times {gmi\tx..?, x..?, x..?\n} 1 } } */
+/* { dg-final { scan-assembler-times {subp\tx..?, x..?, x..?\n} 1 } } */
+/* { dg-final { scan-assembler-times {addg\tx..?, x..?, #0, #1\n} 1 } } */
+/* { dg-final { scan-assembler-times {ldg\tx..?, \[x..?, #0\]\n} 1 } } */
+/* { dg-final { scan-assembler-times {stg\tx..?, \[x..?, #0\]\n} 1 } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/aarch64/acle/memtag_2.c b/gcc/testsuite/gcc.target/aarch64/acle/memtag_2.c
new file mode 100644
index 00000000000..da2fdf39ba1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/acle/memtag_2.c
@@ -0,0 +1,69 @@
+/* Test the MEMTAG intrinsic qualifier warnings and argument errors.  */
+
+/* { dg-do compile { target aarch64-*-* } } */
+/* { dg-options "-O2 -march=armv8.5-a+memtag" } */
+
+#include "arm_acle.h"
+
+void
+test_memtag_warning_return_qualifier (void)
+{
+  const char *c;
+  volatile char *v;
+  char *n;
+  int *i;
+  int64_t d;
+
+  v = __arm_mte_get_tag(c);		  /* { dg-warning {assignment} } */
+  n = __arm_mte_get_tag(c);		  /* { dg-warning {assignment} } */
+  i = __arm_mte_get_tag(c);		  /* { dg-warning {assignment} } */
+  c = __arm_mte_get_tag(v);		  /* { dg-warning {assignment} } */
+  n = __arm_mte_get_tag(v);		  /* { dg-warning {assignment} } */
+
+  i = __arm_mte_create_random_tag (c, 0); /* { dg-warning {assignment} } */
+  i = __arm_mte_increment_tag (c, 0);	  /* { dg-warning {assignment} } */
+
+  c = __arm_mte_get_tag(n);		  /* No warning.  */
+  d = __arm_mte_ptrdiff(c, i);		  /* No warning.  */
+}
+
+void
+test_memtag_warning_argument (void)
+{
+  const char *c;
+  uint64_t i;
+  __arm_mte_exclude_tag(i, 0);		/* { dg-warning {argument} } */
+  __arm_mte_create_random_tag (i, 0);	/* { dg-warning {argument} } */
+  __arm_mte_set_tag(i);			/* { dg-warning {argument} } */
+  __arm_mte_get_tag(i);			/* { dg-warning {argument} } */
+  __arm_mte_increment_tag (i, 15);	/* { dg-warning {argument} } */
+  __arm_mte_ptrdiff(c, i);		/* { dg-warning {argument} } */
+  __arm_mte_ptrdiff(i, c);		/* { dg-warning {argument} } */
+
+  __arm_mte_exclude_tag(1, 0);		/* { dg-warning {argument} } */
+  __arm_mte_create_random_tag (1, 0);	/* { dg-warning {argument} } */
+  __arm_mte_set_tag(1);			/* { dg-warning {argument} } */
+  __arm_mte_get_tag(1);			/* { dg-warning {argument} } */
+  __arm_mte_increment_tag (1, 15);	/* { dg-warning {argument} } */
+  __arm_mte_ptrdiff(c, 1);		/* { dg-warning {argument} } */
+  __arm_mte_ptrdiff(1, c);		/* { dg-warning {argument} } */
+
+  __arm_mte_exclude_tag(0, 0);		/* No warning.  */
+  __arm_mte_create_random_tag (0, 0);	/* No warning.  */
+  __arm_mte_set_tag(0);			/* No warning.  */
+  __arm_mte_get_tag(0);			/* No warning.  */
+  __arm_mte_increment_tag (0, 15);	/* No warning.  */
+  __arm_mte_ptrdiff(c, 0);		/* No warning.  */
+  __arm_mte_ptrdiff(0, c);		/* No warning.  */
+}
+
+void
+test_memtag_error_argument (void)
+{
+  /* Produce errors properly for invalid arguments.  */
+  __arm_mte_exclude_tag(no_decl, 0);	/* { dg-error {} } */
+  __arm_mte_exclude_tag();		/* { dg-error {} } */
+  __arm_mte_ptrdiff(no_decl2, 0);	/* { dg-error {} } */
+  __arm_mte_ptrdiff(0);			/* { dg-error {} } */
+  __arm_mte_ptrdiff();			/* { dg-error {} } */
+}
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/aarch64/acle/memtag_3.c b/gcc/testsuite/gcc.target/aarch64/acle/memtag_3.c
new file mode 100644
index 00000000000..20b58708145
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/acle/memtag_3.c
@@ -0,0 +1,15 @@
+/* Test the MEMTAG intrinsic expanding errors.  */
+
+/* { dg-do compile { target aarch64-*-* } } */
+/* { dg-options "-O2 -march=armv8.5-a+memtag" } */
+
+#include "arm_acle.h"
+
+void
+test_memtag_error_expand (int i)
+{
+  const char *p;
+  p = __arm_mte_increment_tag (p, -1);	/* { dg-error {in range \[0,15\]} } */
+  p = __arm_mte_increment_tag (p, 16);	/* { dg-error {in range \[0,15\]} } */
+  p = __arm_mte_increment_tag (p, i);	/* { dg-error {constant immediate} } */
+}
\ No newline at end of file

Reply via email to