public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: David Faust <david.faust@oracle.com>
To: gcc-patches@gcc.gnu.org
Cc: jose.marchesi@oracle.com
Subject: [PATCH] bpf: add preserve_field_info builtin
Date: Wed, 19 Oct 2022 15:06:38 -0700	[thread overview]
Message-ID: <20221019220638.13422-1-david.faust@oracle.com> (raw)

Add BPF __builtin_preserve_field_info. This builtin is used to extract
information to facilitate struct and union relocations performed by the
BPF loader, especially for bitfields.

The builtin has the following signature:

  unsigned int __builtin_preserve_field_info (EXPR, unsigned int KIND);

Where EXPR is an expression accessing a field of a struct or union.
Depending on KIND, different information is returned to the program. The
supported values for KIND are as follows:

  enum {
    FIELD_BYTE_OFFSET = 0,
    FIELD_BYTE_SIZE,
    FIELD_EXISTENCE,
    FIELD_SIGNEDNESS,
    FIELD_LSHIFT_U64,
    FIELD_RSHIFT_U64
  };

If -mco-re is in effect (explicitly or implicitly specified), a CO-RE
relocation is added for the access in EXPR recording the relevant
information according to KIND.

Tested on bpf-unknown-none, no known regressions.
OK?

Thanks

gcc/

	* config/bpf/bpf.cc: Support __builtin_preserve_field_info.
	(enum bpf_builtins): Add new builtin.
	(bpf_init_builtins): Likewise.
	(bpf_core_field_info): New function.
	(bpf_expand_builtin): Accomodate new builtin. Refactor adding new
	relocation to...
	(maybe_make_core_relo): ... here. New function.
	(bpf_resolve_overloaded_builtin): Accomodate new builtin.
	(bpf_core_newdecl): Likewise.
	(bpf_core_walk): Likewise.
	(bpf_core_is_maybe_aggregate_access): Improve logic.
	(struct core_walk_data): New.
	* config/bpf/coreout.cc (bpf_core_reloc_add): Allow adding different
	relocation kinds.
	* config/bpf/coreout.h: Analogous change.
	* doc/extend.texi: Document BPF __builtin_preserve_field_info.

gcc/testsuite/

	* gcc.target/bpf/core-builtin-fieldinfo-existence-1.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-lshift-1-be.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-lshift-1-le.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-lshift-2.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-offset-1.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-rshift-1.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-rshift-2.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-sign-1.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-sign-2.c: New test.
	* gcc.target/bpf/core-builtin-fieldinfo-size-1.c: New test.
---
 gcc/config/bpf/bpf.cc                         | 327 ++++++++++++++----
 gcc/config/bpf/coreout.cc                     |   5 +-
 gcc/config/bpf/coreout.h                      |   2 +-
 gcc/doc/extend.texi                           |  21 ++
 .../bpf/core-builtin-fieldinfo-existence-1.c  |  34 ++
 .../bpf/core-builtin-fieldinfo-lshift-1-be.c  |  37 ++
 .../bpf/core-builtin-fieldinfo-lshift-1-le.c  |  37 ++
 .../bpf/core-builtin-fieldinfo-lshift-2.c     |  37 ++
 .../bpf/core-builtin-fieldinfo-offset-1.c     |  56 +++
 .../bpf/core-builtin-fieldinfo-rshift-1.c     |  36 ++
 .../bpf/core-builtin-fieldinfo-rshift-2.c     |  35 ++
 .../bpf/core-builtin-fieldinfo-sign-1.c       |  33 ++
 .../bpf/core-builtin-fieldinfo-sign-2.c       |  45 +++
 .../bpf/core-builtin-fieldinfo-size-1.c       |  43 +++
 14 files changed, 676 insertions(+), 72 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-existence-1.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-1-be.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-1-le.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-2.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-offset-1.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-rshift-1.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-rshift-2.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-1.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-2.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-size-1.c

diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
index 51055651707..41a506349b2 100644
--- a/gcc/config/bpf/bpf.cc
+++ b/gcc/config/bpf/bpf.cc
@@ -184,13 +184,13 @@ enum bpf_builtins
 
   /* Compile Once - Run Everywhere (CO-RE) support.  */
   BPF_BUILTIN_PRESERVE_ACCESS_INDEX,
+  BPF_BUILTIN_PRESERVE_FIELD_INFO,
 
   BPF_BUILTIN_MAX,
 };
 
 static GTY (()) tree bpf_builtins[(int) BPF_BUILTIN_MAX];
 
-
 void bpf_register_coreattr_pass (void);
 
 /* Initialize the per-function machine status.  */
@@ -966,6 +966,9 @@ bpf_init_builtins (void)
   def_builtin ("__builtin_preserve_access_index",
 	       BPF_BUILTIN_PRESERVE_ACCESS_INDEX,
 	       build_function_type_list (ptr_type_node, ptr_type_node, 0));
+  def_builtin ("__builtin_preserve_field_info",
+	       BPF_BUILTIN_PRESERVE_FIELD_INFO,
+	       build_function_type_list (unsigned_type_node, ptr_type_node, unsigned_type_node, 0));
 }
 
 #undef TARGET_INIT_BUILTINS
@@ -975,6 +978,161 @@ static tree bpf_core_compute (tree, vec<unsigned int> *);
 static int bpf_core_get_index (const tree);
 static bool is_attr_preserve_access (tree);
 
+static void
+maybe_make_core_relo (tree expr, enum btf_core_reloc_kind kind)
+{
+  /* If we are not targetting BPF CO-RE, do not make a relocation. We
+     might not be generating any debug info at all.  */
+  if (!TARGET_BPF_CORE)
+    return;
+
+  auto_vec<unsigned int, 16> accessors;
+  tree container = bpf_core_compute (expr, &accessors);
+
+  /* Any valid use of the builtin must have at least one access. Otherwise,
+     there is nothing to record and nothing to do. This is primarily a
+     guard against optimizations leading to unexpected expressions in the
+     argument of the builtin. For example, if the builtin is used to read
+     a field of a structure which can be statically determined to hold a
+     constant value, the argument to the builtin will be optimized to that
+     constant. This is OK, and means the builtin call is superfluous.
+     e.g.
+     struct S foo;
+     foo.a = 5;
+     int x = __preserve_access_index (foo.a);
+     ... do stuff with x
+     'foo.a' in the builtin argument will be optimized to '5' with -01+.
+     This sequence does not warrant recording a CO-RE relocation.  */
+
+  if (accessors.length () < 1)
+    return;
+  accessors.reverse ();
+
+  rtx_code_label *label = gen_label_rtx ();
+  LABEL_PRESERVE_P (label) = 1;
+  emit_label (label);
+
+  /* Determine what output section this relocation will apply to.
+     If this function is associated with a section, use that. Otherwise,
+     fall back on '.text'.  */
+  const char * section_name;
+  if (current_function_decl && DECL_SECTION_NAME (current_function_decl))
+    section_name = DECL_SECTION_NAME (current_function_decl);
+  else
+    section_name = ".text";
+
+  /* Add the CO-RE relocation information to the BTF container.  */
+  bpf_core_reloc_add (TREE_TYPE (container), section_name, &accessors, label,
+		      kind);
+}
+
+/* Expand a call to __builtin_preserve_field_info by evaluating the requested
+   information about SRC according to KIND, and return a tree holding
+   the result.  */
+
+static tree
+bpf_core_field_info (tree src, enum btf_core_reloc_kind kind)
+{
+  unsigned int result;
+  poly_int64 bitsize, bitpos;
+  tree var_off;
+  machine_mode mode;
+  int unsignedp, reversep, volatilep;
+
+  get_inner_reference (src, &bitsize, &bitpos, &var_off, &mode, &unsignedp,
+		       &reversep, &volatilep);
+
+  /* Note: Use DECL_BIT_FIELD_TYPE rather than DECL_BIT_FIELD here, because it
+     remembers whether the field in question was originally declared as a
+     bitfield, regardless of how it has been optimized.  */
+  bool bitfieldp = (TREE_CODE (src) == COMPONENT_REF
+		    && DECL_BIT_FIELD_TYPE (TREE_OPERAND (src, 1)));
+
+  unsigned int align = TYPE_ALIGN (TREE_TYPE (src));
+  if (TREE_CODE (src) == COMPONENT_REF)
+    {
+      tree field = TREE_OPERAND (src, 1);
+      if (DECL_BIT_FIELD_TYPE (field))
+	align = TYPE_ALIGN (DECL_BIT_FIELD_TYPE (field));
+      else
+	align = TYPE_ALIGN (TREE_TYPE (field));
+    }
+
+  unsigned int start_bitpos = bitpos & ~(align - 1);
+  unsigned int end_bitpos = start_bitpos + align;
+
+  switch (kind)
+    {
+    case BPF_RELO_FIELD_BYTE_OFFSET:
+      {
+	if (bitfieldp)
+	  result = start_bitpos / 8;
+	else
+	  result = bitpos / 8;
+      }
+      break;
+
+    case BPF_RELO_FIELD_BYTE_SIZE:
+      {
+	if (bitfieldp)
+	  {
+	    /* To match LLVM behavior, byte size of bitfields is recorded as
+	       the full size of the base type. A 3-bit bitfield of type int is
+	       therefore recorded as having a byte size of 4 bytes. */
+	    result = end_bitpos - start_bitpos;
+	    if (result & (result - 1))
+	      error ("unsupported field expression");
+	    result = result / 8;
+	  }
+	else
+	  result = bitsize / 8;
+      }
+      break;
+
+    case BPF_RELO_FIELD_EXISTS:
+      /* The field always exists at compile time.  */
+      result = 1;
+      break;
+
+    case BPF_RELO_FIELD_SIGNED:
+      result = !unsignedp;
+      break;
+
+    case BPF_RELO_FIELD_LSHIFT_U64:
+    case BPF_RELO_FIELD_RSHIFT_U64:
+      {
+	if (!bitfieldp)
+	  {
+	    if (bitsize > 64)
+	      error ("field size too large");
+	    result = 64 - bitsize;
+	    break;
+	  }
+
+	if (end_bitpos - start_bitpos > 64)
+	  error ("field size too large");
+
+	if (kind == BPF_RELO_FIELD_LSHIFT_U64)
+	  {
+	    if (TARGET_BIG_ENDIAN)
+	      result = bitpos + 64 - start_bitpos - align;
+	    else
+	      result = start_bitpos + 64 - bitpos - bitsize;
+	  }
+	else /* RSHIFT_U64 */
+	  result = 64 - bitsize;
+      }
+      break;
+
+    default:
+      error ("invalid argument to built-in function");
+      return error_mark_node;
+      break;
+    }
+
+  return build_int_cst (unsigned_type_node, result);
+}
+
 /* Expand a call to a BPF-specific built-in function that was set up
    with bpf_init_builtins.  */
 
@@ -1025,17 +1183,15 @@ bpf_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
       /* The result of the load is in R0.  */
       return gen_rtx_REG (ops[0].mode, BPF_R0);
     }
+
   else if (code == -1)
     {
-      /* A resolved overloaded builtin, e.g. __bpf_preserve_access_index_si */
+      /* A resolved overloaded __builtin_preserve_access_index.  */
       tree arg = CALL_EXPR_ARG (exp, 0);
 
       if (arg == NULL_TREE)
 	return NULL_RTX;
 
-      auto_vec<unsigned int, 16> accessors;
-      tree container;
-
       if (TREE_CODE (arg) == SSA_NAME)
 	{
 	  gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
@@ -1049,51 +1205,42 @@ bpf_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
       /* Avoid double-recording information if the argument is an access to
 	 a struct/union marked __attribute__((preserve_access_index)). This
 	 Will be handled by the attribute handling pass.  */
-      if (is_attr_preserve_access (arg))
-	return expand_normal (arg);
-
-      container = bpf_core_compute (arg, &accessors);
-
-      /* Any valid use of the builtin must have at least one access. Otherwise,
-	 there is nothing to record and nothing to do. This is primarily a
-	 guard against optimizations leading to unexpected expressions in the
-	 argument of the builtin. For example, if the builtin is used to read
-	 a field of a structure which can be statically determined to hold a
-	 constant value, the argument to the builtin will be optimized to that
-	 constant. This is OK, and means the builtin call is superfluous.
-	 e.g.
-	   struct S foo;
-	   foo.a = 5;
-	   int x = __preserve_access_index (foo.a);
-	   ... do stuff with x
-	 'foo.a' in the builtin argument will be optimized to '5' with -01+.
-	 This sequence does not warrant recording a CO-RE relocation.  */
-
-      if (accessors.length () < 1)
-	return expand_normal (arg);
-
-      accessors.reverse ();
-
-      container = TREE_TYPE (container);
-
-      rtx_code_label *label = gen_label_rtx ();
-      LABEL_PRESERVE_P (label) = 1;
-      emit_label (label);
-
-      /* Determine what output section this relocation will apply to.
-	 If this function is associated with a section, use that. Otherwise,
-	 fall back on '.text'.  */
-      const char * section_name;
-      if (current_function_decl && DECL_SECTION_NAME (current_function_decl))
-	section_name = DECL_SECTION_NAME (current_function_decl);
+      if (!is_attr_preserve_access (arg))
+	maybe_make_core_relo (arg, BPF_RELO_FIELD_BYTE_OFFSET);
+
+      return expand_normal (arg);
+    }
+
+  else if (code == -2)
+    {
+      /* A resolved overloaded __builtin_preserve_field_info.  */
+      tree src = CALL_EXPR_ARG (exp, 0);
+      tree kind_tree = CALL_EXPR_ARG (exp, 1);
+      unsigned HOST_WIDE_INT kind_val;
+      if (tree_fits_uhwi_p (kind_tree))
+	kind_val = tree_to_uhwi (kind_tree);
       else
-	section_name = ".text";
+	error ("invalid argument to built-in function");
 
-      /* Add the CO-RE relocation information to the BTF container.  */
-      bpf_core_reloc_add (container, section_name, &accessors, label);
+      enum btf_core_reloc_kind kind = (enum btf_core_reloc_kind) kind_val;
 
-      return expand_normal (arg);
+      if (TREE_CODE (src) == SSA_NAME)
+	{
+	  gimple *def_stmt = SSA_NAME_DEF_STMT (src);
+	  if (is_gimple_assign (def_stmt))
+	    src = gimple_assign_rhs1 (def_stmt);
+	}
+      if (TREE_CODE (src) == ADDR_EXPR)
+	src = TREE_OPERAND (src, 0);
+
+      tree result = bpf_core_field_info (src, kind);
+
+      if (result != error_mark_node)
+	maybe_make_core_relo (src, kind);
+
+      return expand_normal (result);
     }
+
   gcc_unreachable ();
 }
 
@@ -1259,17 +1406,29 @@ bpf_core_get_index (const tree node)
    __builtin_preserve_access_index.  */
 
 static tree
-bpf_core_newdecl (tree type)
+bpf_core_newdecl (tree type, bool is_pai)
 {
-  tree rettype = build_function_type_list (type, type, NULL);
+  tree rettype;
   char name[80];
-  int len = snprintf (name, sizeof (name), "%s", "__builtin_pai_");
+  static unsigned long pai_count = 0;
+  static unsigned long pfi_count = 0;
 
-  static unsigned long cnt = 0;
-  len = snprintf (name + len, sizeof (name) - len, "%lu", cnt++);
+  if (is_pai)
+    {
+      rettype = build_function_type_list (type, type, NULL);
+      int len = snprintf (name, sizeof (name), "%s", "__builtin_pai_");
+      len = snprintf (name + len, sizeof (name) - len, "%lu", pai_count++);
+    }
+  else
+    {
+      rettype = build_function_type_list (unsigned_type_node, type,
+					  unsigned_type_node, NULL);
+      int len = snprintf (name, sizeof (name), "%s", "__builtin_pfi_");
+      len = snprintf (name + len, sizeof (name) - len, "%lu", pfi_count++);
+    }
 
-  return add_builtin_function_ext_scope (name, rettype, -1, BUILT_IN_MD, NULL,
-					 NULL_TREE);
+  return add_builtin_function_ext_scope (name, rettype, is_pai ? -1 : -2,
+					 BUILT_IN_MD, NULL, NULL_TREE);
 }
 
 /* Return whether EXPR could access some aggregate data structure that
@@ -1278,22 +1437,33 @@ bpf_core_newdecl (tree type)
 static int
 bpf_core_is_maybe_aggregate_access (tree expr)
 {
-  enum tree_code code = TREE_CODE (expr);
-  if (code == COMPONENT_REF || code == ARRAY_REF)
-    return 1;
-
-  if (code == ADDR_EXPR)
+  switch (TREE_CODE (expr))
+    {
+    case COMPONENT_REF:
+    case BIT_FIELD_REF:
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      return 1;
+    case ADDR_EXPR:
+    case NOP_EXPR:
       return bpf_core_is_maybe_aggregate_access (TREE_OPERAND (expr, 0));
-
+    default:;
+    }
   return 0;
 }
 
+struct core_walk_data {
+  location_t loc;
+  tree arg;
+};
+
 /* Callback function used with walk_tree from bpf_resolve_overloaded_builtin.  */
 
 static tree
 bpf_core_walk (tree *tp, int *walk_subtrees, void *data)
 {
-  location_t loc = *((location_t *) data);
+  struct core_walk_data *dat = (struct core_walk_data *) data;
+  bool is_pai = dat->arg == NULL_TREE;
 
   /* If this is a type, don't do anything. */
   if (TYPE_P (*tp))
@@ -1302,10 +1472,18 @@ bpf_core_walk (tree *tp, int *walk_subtrees, void *data)
       return NULL_TREE;
     }
 
+  /* Build a new function call to a resolved builtin for the desired operation.
+     If this is a preserve_field_info call, pass along the argument to the
+     resolved builtin call. */
   if (bpf_core_is_maybe_aggregate_access (*tp))
     {
-      tree newdecl = bpf_core_newdecl (TREE_TYPE (*tp));
-      tree newcall = build_call_expr_loc (loc, newdecl, 1, *tp);
+      tree newdecl = bpf_core_newdecl (TREE_TYPE (*tp), is_pai);
+      tree newcall;
+      if (is_pai)
+	newcall = build_call_expr_loc (dat->loc, newdecl, 1, *tp);
+      else
+	newcall = build_call_expr_loc (dat->loc, newdecl, 2, *tp, dat->arg);
+
       *tp = newcall;
       *walk_subtrees = 0;
     }
@@ -1344,7 +1522,12 @@ bpf_small_register_classes_for_mode_p (machine_mode mode)
 static tree
 bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
 {
-  if (DECL_MD_FUNCTION_CODE (fndecl) != BPF_BUILTIN_PRESERVE_ACCESS_INDEX)
+  bool is_pai = DECL_MD_FUNCTION_CODE (fndecl)
+    == BPF_BUILTIN_PRESERVE_ACCESS_INDEX;
+  bool is_pfi = DECL_MD_FUNCTION_CODE (fndecl)
+    == BPF_BUILTIN_PRESERVE_FIELD_INFO;
+
+  if (!is_pai && !is_pfi)
     return NULL_TREE;
 
   /* We only expect one argument, but it may be an arbitrarily-complicated
@@ -1352,16 +1535,17 @@ bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
   vec<tree, va_gc> *params = static_cast<vec<tree, va_gc> *> (arglist);
   unsigned n_params = params ? params->length() : 0;
 
-  if (n_params != 1)
+  if ((is_pai && n_params != 1) || (is_pfi && n_params != 2))
     {
-      error_at (loc, "expected exactly 1 argument");
+      error_at (loc, "wrong number of arguments");
       return NULL_TREE;
     }
 
   tree param = (*params)[0];
 
-  /* If not generating BPF_CORE information, the builtin does nothing.  */
-  if (!TARGET_BPF_CORE)
+  /* If not generating BPF_CORE information, preserve_access_index does nothing,
+     and simply "resolves to" the argument.  */
+  if (!TARGET_BPF_CORE && is_pai)
     return param;
 
   /* Do remove_c_maybe_const_expr for the arg.
@@ -1387,7 +1571,11 @@ bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
      This ensures that all the relevant information remains within the
      expression trees the builtin finally gets.  */
 
-  walk_tree (&param, bpf_core_walk, (void *) &loc, NULL);
+  struct core_walk_data data;
+  data.loc = loc;
+  data.arg = is_pai ? NULL_TREE : (*params)[1];
+
+  walk_tree (&param, bpf_core_walk, (void *) &data, NULL);
 
   return param;
 }
@@ -1524,7 +1712,8 @@ handle_attr_preserve (function *fn)
 		      emit_label (label);
 
 		      /* Add the CO-RE relocation information to the BTF container.  */
-		      bpf_core_reloc_add (container, section_name, &accessors, label);
+		      bpf_core_reloc_add (container, section_name, &accessors, label,
+					  BPF_RELO_FIELD_BYTE_OFFSET);
 		    }
 		}
 	    }
diff --git a/gcc/config/bpf/coreout.cc b/gcc/config/bpf/coreout.cc
index 8897a045ea1..9f71040846b 100644
--- a/gcc/config/bpf/coreout.cc
+++ b/gcc/config/bpf/coreout.cc
@@ -152,7 +152,8 @@ static GTY (()) vec<bpf_core_section_ref, va_gc> *bpf_core_sections;
 
 void
 bpf_core_reloc_add (const tree type, const char * section_name,
-		    vec<unsigned int> *accessors, rtx_code_label *label)
+		    vec<unsigned int> *accessors, rtx_code_label *label,
+		    enum btf_core_reloc_kind kind)
 {
   char buf[40];
   unsigned int i, n = 0;
@@ -173,7 +174,7 @@ bpf_core_reloc_add (const tree type, const char * section_name,
 
   bpfcr->bpfcr_type = get_btf_id (ctf_lookup_tree_type (ctfc, type));
   bpfcr->bpfcr_insn_label = label;
-  bpfcr->bpfcr_kind = BPF_RELO_FIELD_BYTE_OFFSET;
+  bpfcr->bpfcr_kind = kind;
 
   /* Add the CO-RE reloc to the appropriate section.  */
   bpf_core_section_ref sec;
diff --git a/gcc/config/bpf/coreout.h b/gcc/config/bpf/coreout.h
index 3c7bdfd8c2f..498853f6e00 100644
--- a/gcc/config/bpf/coreout.h
+++ b/gcc/config/bpf/coreout.h
@@ -103,7 +103,7 @@ extern void btf_ext_init (void);
 extern void btf_ext_output (void);
 
 extern void bpf_core_reloc_add (const tree, const char *, vec<unsigned int> *,
-				rtx_code_label *);
+				rtx_code_label *, enum btf_core_reloc_kind);
 extern int bpf_core_get_sou_member_index (ctf_container_ref, const tree);
 
 #ifdef	__cplusplus
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 04af0584d82..0d3bcb24ab9 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -15745,6 +15745,27 @@ Load 32-bits from the @code{struct sk_buff} packet data pointed by the register
 BPF Compile Once-Run Everywhere (CO-RE) support. Instruct GCC to generate CO-RE relocation records for any accesses to aggregate data structures (struct, union, array types) in @var{expr}. This builtin is otherwise transparent, the return value is whatever @var{expr} evaluates to. It is also overloaded: @var{expr} may be of any type (not necessarily a pointer), the return type is the same. Has no effect if @code{-mco-re} is not in effect (either specified or implied).
 @end deftypefn
 
+@deftypefn {Built-in Function} unsigned int __builtin_preserve_field_info (@var{expr}, unsigned int @var{kind})
+BPF Compile Once-Run Everywhere (CO-RE) support. This builtin is used to
+extract information to aid in struct/union relocations.  @var{expr} is
+an access to a field of a struct or union. Depending on @var{kind}, different
+information is returned to the program. A CO-RE relocation for the access in
+@var{expr} with kind @var{kind} is recorded if @code{-mco-re} is in effect.
+
+@var{kind} is one of:
+@smallexample
+enum
+@{
+  FIELD_BYTE_OFFSET = 0,
+  FIELD_BYTE_SIZE,
+  FIELD_EXISTENCE,
+  FIELD_SIGNEDNESS,
+  FIELD_LSHIFT_U64,
+  FIELD_RSHIFT_U64,
+@};
+@end smallexample
+@end deftypefn
+
 @node FR-V Built-in Functions
 @subsection FR-V Built-in Functions
 
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-existence-1.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-existence-1.c
new file mode 100644
index 00000000000..c55f21a9c11
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-existence-1.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+enum {
+  FIELD_EXISTENCE = 2,
+};
+
+typedef unsigned uint;
+
+struct S {
+  unsigned char c;
+  int d;
+  uint u;
+  short ar[3];
+};
+
+unsigned int foo (struct S *s)
+{
+  unsigned c  = __builtin_preserve_field_info (s->c, FIELD_EXISTENCE);
+  unsigned d  = __builtin_preserve_field_info (s->d, FIELD_EXISTENCE);
+  unsigned u  = __builtin_preserve_field_info (s->u, FIELD_EXISTENCE);
+  unsigned ar = __builtin_preserve_field_info (s->ar[1], FIELD_EXISTENCE);
+
+  return c + d + u + ar;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],1" 4 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:3:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0x2\[\t \]+\[^\n\]*bpfcr_kind" 4 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-1-be.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-1-be.c
new file mode 100644
index 00000000000..dabf73dd259
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-1-be.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re -mbig-endian" } */
+
+struct S {
+  int x1: 6;
+  int x2: 3;
+  int x3: 7;
+  int x4: 16;
+};
+
+enum {
+  FIELD_LSHIFT_U64 = 4,
+};
+
+unsigned int foo (struct S *s)
+{
+  /* little endian: x1=58, x2=55, x3=48, x4=32 */
+  /* big endian:    x1=32, x2=38, x3=41, x4=48 */
+  unsigned x1 = __builtin_preserve_field_info (s->x1, FIELD_LSHIFT_U64);
+  unsigned x2 = __builtin_preserve_field_info (s->x2, FIELD_LSHIFT_U64);
+  unsigned x3 = __builtin_preserve_field_info (s->x3, FIELD_LSHIFT_U64);
+  unsigned x4 = __builtin_preserve_field_info (s->x4, FIELD_LSHIFT_U64);
+
+  return x1 + x2 + x3 + x4;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],32" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],38" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],41" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],48" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0x4\[\t \]+\[^\n\]*bpfcr_kind" 4 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-1-le.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-1-le.c
new file mode 100644
index 00000000000..99e3982d932
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-1-le.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re -mlittle-endian" } */
+
+struct S {
+  int x1: 6;
+  int x2: 3;
+  int x3: 7;
+  int x4: 16;
+};
+
+enum {
+  FIELD_LSHIFT_U64 = 4,
+};
+
+unsigned int foo (struct S *s)
+{
+  /* little endian: x1=58, x2=55, x3=48, x4=32 */
+  /* big endian:    x1=32, x2=38, x3=41, x4=48 */
+  unsigned x1 = __builtin_preserve_field_info (s->x1, FIELD_LSHIFT_U64);
+  unsigned x2 = __builtin_preserve_field_info (s->x2, FIELD_LSHIFT_U64);
+  unsigned x3 = __builtin_preserve_field_info (s->x3, FIELD_LSHIFT_U64);
+  unsigned x4 = __builtin_preserve_field_info (s->x4, FIELD_LSHIFT_U64);
+
+  return x1 + x2 + x3 + x4;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],58" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],55" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],48" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],32" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0x4\[\t \]+\[^\n\]*bpfcr_kind" 4 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-2.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-2.c
new file mode 100644
index 00000000000..25be969e22b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-lshift-2.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct S {
+  char c;
+  short s;
+  int x;
+};
+
+union U {
+  struct S s[2];
+  long long ll;
+};
+
+enum {
+  FIELD_LSHIFT_U64 = 4,
+};
+
+unsigned int foo (union U *u)
+{
+  /* s0s = 48, s1c = 56, ll = 0; endianness independent.  */
+  unsigned s0s = __builtin_preserve_field_info (u->s[0].s, FIELD_LSHIFT_U64);
+  unsigned s1c = __builtin_preserve_field_info (u->s[1].c, FIELD_LSHIFT_U64);
+  unsigned ll  = __builtin_preserve_field_info (u->ll, FIELD_LSHIFT_U64);
+
+  return s0s + s1c + ll;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],48" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],56" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],0" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:0:0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:0:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0x4\[\t \]+\[^\n\]*bpfcr_kind" 3 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-offset-1.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-offset-1.c
new file mode 100644
index 00000000000..590eea007ae
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-offset-1.c
@@ -0,0 +1,56 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct S {
+  unsigned int a1: 7;
+  unsigned int a2: 4;
+  unsigned int a3: 13;
+  unsigned int a4: 5;
+  int x;
+};
+
+struct T {
+  unsigned int y;
+  struct S s[2];
+  char c;
+  char d;
+};
+
+enum {
+  FIELD_BYTE_OFFSET = 0,
+};
+
+
+unsigned int foo (struct T *t)
+{
+  unsigned s0a1 = __builtin_preserve_field_info (t->s[0].a1, FIELD_BYTE_OFFSET);
+  unsigned s0a4 = __builtin_preserve_field_info (t->s[0].a4, FIELD_BYTE_OFFSET);
+  unsigned s0x  = __builtin_preserve_field_info (t->s[0].x, FIELD_BYTE_OFFSET);
+
+  unsigned s1a1 = __builtin_preserve_field_info (t->s[1].a1, FIELD_BYTE_OFFSET);
+  unsigned s1a4 = __builtin_preserve_field_info (t->s[1].a4, FIELD_BYTE_OFFSET);
+  unsigned s1x  = __builtin_preserve_field_info (t->s[1].x, FIELD_BYTE_OFFSET);
+
+  unsigned c = __builtin_preserve_field_info (t->c, FIELD_BYTE_OFFSET);
+  unsigned d = __builtin_preserve_field_info (t->d, FIELD_BYTE_OFFSET);
+
+  return s0a1 + s0a4 + s0x + s1a1 + s1a4 + s1x + c + d;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],4" 2 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],8" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],12" 2 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],16" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],20" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],21" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:1:0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:0:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0\[\t \]+\[^\n\]*bpfcr_kind" 8 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-rshift-1.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-rshift-1.c
new file mode 100644
index 00000000000..d0c75d944cd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-rshift-1.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct S {
+  int x1: 6;
+  int x2: 3;
+  int x3: 7;
+  int x4: 16;
+};
+
+enum {
+  FIELD_RSHIFT_U64 = 5,
+};
+
+unsigned int foo (struct S *s)
+{
+  /* x1=58, x2=61, x3=57, x4=48; endianness independent.  */
+  unsigned x1 = __builtin_preserve_field_info (s->x1, FIELD_RSHIFT_U64);
+  unsigned x2 = __builtin_preserve_field_info (s->x2, FIELD_RSHIFT_U64);
+  unsigned x3 = __builtin_preserve_field_info (s->x3, FIELD_RSHIFT_U64);
+  unsigned x4 = __builtin_preserve_field_info (s->x4, FIELD_RSHIFT_U64);
+
+  return x1 + x2 + x3 + x4;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],58" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],61" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],57" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],48" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0x5\[\t \]+\[^\n\]*bpfcr_kind" 4 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-rshift-2.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-rshift-2.c
new file mode 100644
index 00000000000..a71ddc17728
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-rshift-2.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct S {
+  int x;
+  char c;
+};
+
+union U {
+  int i;
+  struct S s;
+};
+
+enum {
+  FIELD_RSHIFT_U64 = 5,
+};
+
+unsigned int foo (union U *u)
+{
+  /* sx = 32, sc = 56, i = 32; endianness independent.  */
+  unsigned sx = __builtin_preserve_field_info (u->s.x, FIELD_RSHIFT_U64);
+  unsigned sc = __builtin_preserve_field_info (u->s.c, FIELD_RSHIFT_U64);
+  unsigned i  = __builtin_preserve_field_info (u->i, FIELD_RSHIFT_U64);
+
+  return sx + sc + i;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],32" 2 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],56" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0x5\[\t \]+\[^\n\]*bpfcr_kind" 3 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-1.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-1.c
new file mode 100644
index 00000000000..3b2081e197c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-1.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+enum {
+  FIELD_SIGNEDNESS = 3,
+};
+
+typedef unsigned uint;
+
+struct S {
+  unsigned char c;
+  int d;
+  uint u;
+  short ar[3];
+};
+
+unsigned int foo (struct S *s)
+{
+  unsigned d  = __builtin_preserve_field_info (s->d, FIELD_SIGNEDNESS);
+  unsigned u  = __builtin_preserve_field_info (s->u, FIELD_SIGNEDNESS);
+  unsigned ar = __builtin_preserve_field_info (s->ar[1], FIELD_SIGNEDNESS);
+
+  return d + u + ar;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],1" 2 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],0" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:3:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0x3\[\t \]+\[^\n\]*bpfcr_kind" 3 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-2.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-2.c
new file mode 100644
index 00000000000..bf184299984
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-2.c
@@ -0,0 +1,45 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+enum {
+  FIELD_SIGNEDNESS = 3,
+};
+
+enum Esig {
+  SA = -1,
+  SB,
+  SC,
+};
+
+enum Eun {
+  UA = 0,
+  UB,
+};
+
+struct S {
+  enum Esig sig : 3;
+  enum Eun un : 3;
+};
+
+union U {
+  int i;
+  struct S s;
+};
+
+unsigned int foo (union U *u)
+{
+  unsigned i   = __builtin_preserve_field_info (u->i, FIELD_SIGNEDNESS);
+  unsigned sig = __builtin_preserve_field_info (u->s.sig, FIELD_SIGNEDNESS);
+  unsigned un  = __builtin_preserve_field_info (u->s.un, FIELD_SIGNEDNESS);
+
+  return i + sig + un;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],1" 2 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],0" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "3\[\t \]+\[^\n\]*bpfcr_kind" 3 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-size-1.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-size-1.c
new file mode 100644
index 00000000000..8747bdeb9c3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-size-1.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct S {
+  unsigned int a1: 7;
+  unsigned int a2: 4;
+  unsigned int a3: 13;
+  unsigned int a4: 5;
+  char carr[5][3];
+};
+
+enum {
+  FIELD_BYTE_SIZE = 1,
+};
+
+union U {
+  long long l[3];
+  struct S s;
+};
+
+unsigned int foo (union U *u)
+{
+  unsigned ls = __builtin_preserve_field_info (u->l, FIELD_BYTE_SIZE);
+  unsigned s  = __builtin_preserve_field_info (u->s, FIELD_BYTE_SIZE);
+  unsigned a2 = __builtin_preserve_field_info (u->s.a2, FIELD_BYTE_SIZE);
+  unsigned a3 = __builtin_preserve_field_info (u->s.a3, FIELD_BYTE_SIZE);
+  unsigned ca = __builtin_preserve_field_info (u->s.carr, FIELD_BYTE_SIZE);
+
+  return ls + s + a2 + a3 + ca;
+}
+
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],24" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],20" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],4" 2 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],15" 1 } } */
+
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "0x1\[\t \]+\[^\n\]*bpfcr_kind" 5 } } */
-- 
2.37.2


             reply	other threads:[~2022-10-19 22:06 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-19 22:06 David Faust [this message]
2022-10-20 12:38 ` Jose E. Marchesi
2022-10-25 17:24   ` [PATCH v2] " David Faust
2022-10-25 19:11     ` Jose E. Marchesi
2022-10-26 19:23       ` [PATCH v3] " David Faust
2022-10-26 19:33         ` Jose E. Marchesi
2022-10-26 20:21           ` David Faust

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221019220638.13422-1-david.faust@oracle.com \
    --to=david.faust@oracle.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jose.marchesi@oracle.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).