Ok.
Thanks!

> From fda9603ded735205b6e20fc5b65a04f8d15685e6 Mon Sep 17 00:00:00 2001
> From: Cupertino Miranda <cupertino.mira...@oracle.com>
> Date: Thu, 6 Apr 2023 15:22:48 +0100
> Subject: [PATCH v2 1/2] bpf: Implementation of BPF CO-RE builtins
>
> This patch updates the support for the BPF CO-RE builtins
> __builtin_preserve_access_index and __builtin_preserve_field_info,
> and adds support for the CO-RE builtins __builtin_btf_type_id,
> __builtin_preserve_type_info and __builtin_preserve_enum_value.
>
> These CO-RE relocations are now converted to __builtin_core_reloc which
> abstracts all of the original builtins in a polymorphic relocation
> specific builtin.
>
> The builtin processing is now split in 2 stages, the first (pack) is
> executed right after the front-end and the second (process) right before
> the asm output.
>
> In expand pass the __builtin_core_reloc is converted to a
> unspec:UNSPEC_CORE_RELOC rtx entry.
>
> The data required to process the builtin is now collected in the packing
> stage (after front-end), not allowing the compiler to optimize any of
> the relevant information required to compose the relocation when
> necessary.
> At expansion, that information is recovered and CTF/BTF is queried to
> construct the information that will be used in the relocation.
> At this point the relocation is added to specific section and the
> builtin is expanded to the expected default value for the builtin.
>
> In order to process __builtin_preserve_enum_value, it was necessary to
> hook the front-end to collect the original enum value reference.
> This is needed since the parser folds all the enum values to its
> integer_cst representation.
>
> More details can be found within the core-builtins.cc.
>
> Regtested in host x86_64-linux-gnu and target bpf-unknown-none.
> ---
>  gcc/config.gcc                  |    4 +-
>  gcc/config/bpf/bpf-passes.def   |   20 -
>  gcc/config/bpf/bpf-protos.h     |    4 +-
>  gcc/config/bpf/bpf.cc           |  806 ++----------------
>  gcc/config/bpf/bpf.md           |   17 +
>  gcc/config/bpf/core-builtins.cc | 1394 +++++++++++++++++++++++++++++++
>  gcc/config/bpf/core-builtins.h  |   35 +
>  gcc/config/bpf/coreout.cc       |   50 +-
>  gcc/config/bpf/coreout.h        |   13 +-
>  gcc/config/bpf/t-bpf            |    6 +-
>  gcc/doc/extend.texi             |   51 ++
>  11 files changed, 1595 insertions(+), 805 deletions(-)
>  delete mode 100644 gcc/config/bpf/bpf-passes.def
>  create mode 100644 gcc/config/bpf/core-builtins.cc
>  create mode 100644 gcc/config/bpf/core-builtins.h
>
> diff --git a/gcc/config.gcc b/gcc/config.gcc
> index eba69a463be0..c521669e78b1 100644
> --- a/gcc/config.gcc
> +++ b/gcc/config.gcc
> @@ -1597,8 +1597,8 @@ bpf-*-*)
>          use_collect2=no
>          extra_headers="bpf-helpers.h"
>          use_gcc_stdint=provide
> -        extra_objs="coreout.o"
> -        target_gtfiles="$target_gtfiles \$(srcdir)/config/bpf/coreout.cc"
> +        extra_objs="coreout.o core-builtins.o"
> +        target_gtfiles="$target_gtfiles \$(srcdir)/config/bpf/coreout.cc 
> \$(srcdir)/config/bpf/core-builtins.cc"
>          ;;
>  cris-*-elf | cris-*-none)
>       tm_file="elfos.h newlib-stdint.h ${tm_file}"
> diff --git a/gcc/config/bpf/bpf-passes.def b/gcc/config/bpf/bpf-passes.def
> deleted file mode 100644
> index deeaee988a01..000000000000
> --- a/gcc/config/bpf/bpf-passes.def
> +++ /dev/null
> @@ -1,20 +0,0 @@
> -/* Declaration of target-specific passes for eBPF.
> -   Copyright (C) 2021-2023 Free Software Foundation, Inc.
> -
> -   This file is part of GCC.
> -
> -   GCC is free software; you can redistribute it and/or modify it
> -   under the terms of the GNU General Public License as published by
> -   the Free Software Foundation; either version 3, or (at your option)
> -   any later version.
> -
> -   GCC is distributed in the hope that it will be useful, but
> -   WITHOUT ANY WARRANTY; without even the implied warranty of
> -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> -   General Public License for more details.
> -
> -   You should have received a copy of the GNU General Public License
> -   along with GCC; see the file COPYING3.  If not see
> -   <http://www.gnu.org/licenses/>.  */
> -
> -INSERT_PASS_AFTER (pass_df_initialize_opt, 1, pass_bpf_core_attr);
> diff --git a/gcc/config/bpf/bpf-protos.h b/gcc/config/bpf/bpf-protos.h
> index b484310e8cbf..fbe0d8a0213f 100644
> --- a/gcc/config/bpf/bpf-protos.h
> +++ b/gcc/config/bpf/bpf-protos.h
> @@ -30,7 +30,7 @@ extern void bpf_print_operand_address (FILE *, rtx);
>  extern void bpf_expand_prologue (void);
>  extern void bpf_expand_epilogue (void);
>  extern void bpf_expand_cbranch (machine_mode, rtx *);
> -
> -rtl_opt_pass * make_pass_bpf_core_attr (gcc::context *);
> +const char *bpf_add_core_reloc (rtx *operands, const char *templ);
> +void bpf_replace_core_move_operands (rtx *operands);
>  
>  #endif /* ! GCC_BPF_PROTOS_H */
> diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
> index 57817cdf2f86..4873475e73bd 100644
> --- a/gcc/config/bpf/bpf.cc
> +++ b/gcc/config/bpf/bpf.cc
> @@ -69,10 +69,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimplify.h"
>  #include "gimplify-me.h"
>  
> -#include "ctfc.h"
> -#include "btf.h"
> -
> -#include "coreout.h"
> +#include "core-builtins.h"
>  
>  /* Per-function machine data.  */
>  struct GTY(()) machine_function
> @@ -174,22 +171,7 @@ static const struct attribute_spec bpf_attribute_table[] 
> =
>     one.  */
>  #define BPF_BUILTIN_MAX_ARGS 5
>  
> -enum bpf_builtins
> -{
> -  BPF_BUILTIN_UNUSED = 0,
> -  /* Built-ins for non-generic loads and stores.  */
> -  BPF_BUILTIN_LOAD_BYTE,
> -  BPF_BUILTIN_LOAD_HALF,
> -  BPF_BUILTIN_LOAD_WORD,
> -
> -  /* 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];
> +GTY (()) tree bpf_builtins[(int) BPF_BUILTIN_MAX];
>  
>  void bpf_register_coreattr_pass (void);
>  
> @@ -855,6 +837,7 @@ bpf_output_call (rtx target)
>     Additionally, the code 'w' denotes that the register should be printed
>     as wN instead of rN, where N is the register number, but only when the
>     value stored in the operand OP is 32-bit wide.  */
> +
>  static void
>  bpf_print_register (FILE *file, rtx op, int code)
>  {
> @@ -981,13 +964,14 @@ bpf_print_operand_address (FILE *file, rtx addr)
>  /* Add a BPF builtin function with NAME, CODE and TYPE.  Return
>     the function decl or NULL_TREE if the builtin was not added.  */
>  
> -static tree
> +static inline tree
>  def_builtin (const char *name, enum bpf_builtins code, tree type)
>  {
>    tree t
> -    = add_builtin_function (name, type, code, BUILT_IN_MD, NULL, NULL_TREE);
> +    = add_builtin_function (name, type, code, BUILT_IN_MD, NULL, NULL);
>  
>    bpf_builtins[code] = t;
> +
>    return t;
>  }
>  
> @@ -1006,214 +990,40 @@ bpf_init_builtins (void)
>              build_function_type_list (ullt, ullt, 0));
>    def_builtin ("__builtin_bpf_load_word", BPF_BUILTIN_LOAD_WORD,
>              build_function_type_list (ullt, ullt, 0));
> +
>    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));
> +            build_function_type_list (unsigned_type_node, ptr_type_node,
> +                                      unsigned_type_node, 0));
> +  def_builtin ("__builtin_btf_type_id",
> +            BPF_BUILTIN_BTF_TYPE_ID,
> +            build_function_type_list (integer_type_node, ptr_type_node,
> +                                      integer_type_node, 0));
> +  def_builtin ("__builtin_preserve_type_info",
> +            BPF_BUILTIN_PRESERVE_TYPE_INFO,
> +            build_function_type_list (integer_type_node, ptr_type_node,
> +                                      integer_type_node, 0));
> +  def_builtin ("__builtin_preserve_enum_value",
> +            BPF_BUILTIN_PRESERVE_ENUM_VALUE,
> +            build_function_type_list (integer_type_node, ptr_type_node,
> +                                      integer_type_node, integer_type_node,
> +                                      0));
> +
> +  def_builtin ("__builtin_core_reloc",
> +            BPF_BUILTIN_CORE_RELOC,
> +            build_function_type_list (integer_type_node,integer_type_node,
> +                                      0));
> +  DECL_PURE_P (bpf_builtins[BPF_BUILTIN_CORE_RELOC]) = 1;
> +
> +  bpf_init_core_builtins ();
>  }
>  
>  #undef TARGET_INIT_BUILTINS
>  #define TARGET_INIT_BUILTINS bpf_init_builtins
>  
> -static tree bpf_core_compute (tree, vec<unsigned int> *);
> -static int bpf_core_get_index (const tree);
> -static bool is_attr_preserve_access (tree);
> -
> -/* BPF Compile Once - Run Everywhere (CO-RE) support. Construct a CO-RE
> -   relocation record for EXPR of kind KIND to be emitted in the .BTF.ext
> -   section. Does nothing if we are not targetting BPF CO-RE, or if the
> -   constructed relocation would be a no-op.  */
> -
> -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 = NULL_TREE;
> -  machine_mode mode;
> -  int unsignedp, reversep, volatilep;
> -  location_t loc = EXPR_LOCATION (src);
> -
> -  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 (var_off != NULL_TREE)
> -       {
> -         error_at (loc, "unsupported variable field offset");
> -         return error_mark_node;
> -       }
> -
> -     if (bitfieldp)
> -       result = start_bitpos / 8;
> -     else
> -       result = bitpos / 8;
> -      }
> -      break;
> -
> -    case BPF_RELO_FIELD_BYTE_SIZE:
> -      {
> -     if (mode == BLKmode && bitsize == -1)
> -       {
> -         error_at (loc, "unsupported variable size field access");
> -         return error_mark_node;
> -       }
> -
> -     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_at (loc, "unsupported field expression");
> -             return error_mark_node;
> -           }
> -         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 (mode == BLKmode && bitsize == -1)
> -       {
> -         error_at (loc, "unsupported variable size field access");
> -         return error_mark_node;
> -       }
> -     if (var_off != NULL_TREE)
> -       {
> -         error_at (loc, "unsupported variable field offset");
> -         return error_mark_node;
> -       }
> -
> -     if (!bitfieldp)
> -       {
> -         if (bitsize > 64)
> -           {
> -             error_at (loc, "field size too large");
> -             return error_mark_node;
> -           }
> -         result = 64 - bitsize;
> -         break;
> -       }
> -
> -     if (end_bitpos - start_bitpos > 64)
> -       {
> -         error_at (loc, "field size too large");
> -         return error_mark_node;
> -       }
> -
> -     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 second 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.  */
>  
> @@ -1264,73 +1074,34 @@ 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 == -BPF_BUILTIN_PRESERVE_ACCESS_INDEX)
> -    {
> -      /* A resolved overloaded __builtin_preserve_access_index.  */
> -      tree arg = CALL_EXPR_ARG (exp, 0);
> -
> -      if (arg == NULL_TREE)
> -     return NULL_RTX;
> -
> -      if (TREE_CODE (arg) == SSA_NAME)
> -     {
> -       gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
> -
> -       if (is_gimple_assign (def_stmt))
> -         arg = gimple_assign_rhs1 (def_stmt);
> -       else
> -         return expand_normal (arg);
> -     }
> -
> -      /* 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))
> -     maybe_make_core_relo (arg, BPF_RELO_FIELD_BYTE_OFFSET);
> -
> -      return expand_normal (arg);
> -    }
> -
> -  else if (code == -BPF_BUILTIN_PRESERVE_FIELD_INFO)
> +  else
>      {
> -      /* 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 = 0;
> -      if (tree_fits_uhwi_p (kind_tree))
> -     kind_val = tree_to_uhwi (kind_tree);
> -      else
> -     {
> -       error ("invalid argument to built-in function");
> -       return expand_normal (error_mark_node);
> -     }
> -
> -      enum btf_core_reloc_kind kind = (enum btf_core_reloc_kind) kind_val;
> -
> -      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);
> +      rtx ret = bpf_expand_core_builtin (exp, (enum bpf_builtins) code);
> +      if (ret != NULL_RTX)
> +     return ret;
>      }
>  
> +  error ("invalid built-in function at expansion");
>    gcc_unreachable ();
>  }
>  
>  #undef TARGET_EXPAND_BUILTIN
>  #define TARGET_EXPAND_BUILTIN bpf_expand_builtin
>  
> +static tree
> +bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
> +{
> +  int code = DECL_MD_FUNCTION_CODE (fndecl);
> +  if (code > BPF_CORE_BUILTINS_MARKER)
> +    return bpf_resolve_overloaded_core_builtin (loc, fndecl, arglist);
> +  else
> +    return NULL_TREE;
> +}
> +
> +#undef TARGET_RESOLVE_OVERLOADED_BUILTIN
> +#define TARGET_RESOLVE_OVERLOADED_BUILTIN bpf_resolve_overloaded_builtin
> +
> +
>  /* Initialize target-specific function library calls.  This is mainly
>     used to call library-provided soft-fp operations, since eBPF
>     doesn't support floating-point in "hardware".  */
> @@ -1378,214 +1149,6 @@ bpf_debug_unwind_info ()
>  #undef TARGET_ASM_ALIGNED_DI_OP
>  #define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
>  
> -
> -/* BPF Compile Once - Run Everywhere (CO-RE) support routines.
> -
> -   BPF CO-RE is supported in two forms:
> -   - A target builtin, __builtin_preserve_access_index
> -
> -     This builtin accepts a single argument. Any access to an aggregate data
> -     structure (struct, union or array) within the argument will be recorded 
> by
> -     the CO-RE machinery, resulting in a relocation record being placed in 
> the
> -     .BTF.ext section of the output.
> -
> -     It is implemented in bpf_resolve_overloaded_builtin () and
> -     bpf_expand_builtin (), using the supporting routines below.
> -
> -   - An attribute, __attribute__((preserve_access_index))
> -
> -     This attribute can be applied to struct and union types. Any access to a
> -     type with this attribute will be recorded by the CO-RE machinery.
> -
> -     The pass pass_bpf_core_attr, below, implements support for
> -     this attribute.  */
> -
> -/* Traverse the subtree under NODE, which is expected to be some form of
> -   aggregate access the CO-RE machinery cares about (like a read of a member 
> of
> -   a struct or union), collecting access indices for the components and 
> storing
> -   them in the vector referenced by ACCESSORS.
> -
> -   Return the ultimate (top-level) container of the aggregate access. In 
> general,
> -   this will be a VAR_DECL or some kind of REF.
> -
> -   Note that the accessors are computed *in reverse order* of how the BPF
> -   CO-RE machinery defines them. The vector needs to be reversed (or simply
> -   output in reverse order) for the .BTF.ext relocation information.  */
> -
> -static tree
> -bpf_core_compute (tree node, vec<unsigned int> *accessors)
> -{
> -
> -  if (TREE_CODE (node) == ADDR_EXPR)
> -    node = TREE_OPERAND (node, 0);
> -
> -  else if (INDIRECT_REF_P (node)
> -        || TREE_CODE (node) == POINTER_PLUS_EXPR)
> -    {
> -      accessors->safe_push (0);
> -      return TREE_OPERAND (node, 0);
> -    }
> -
> -  while (1)
> -    {
> -      switch (TREE_CODE (node))
> -     {
> -     case COMPONENT_REF:
> -       accessors->safe_push (bpf_core_get_index (TREE_OPERAND (node, 1)));
> -       break;
> -
> -     case ARRAY_REF:
> -     case ARRAY_RANGE_REF:
> -       accessors->safe_push (bpf_core_get_index (node));
> -       break;
> -
> -     case MEM_REF:
> -       accessors->safe_push (bpf_core_get_index (node));
> -       if (TREE_CODE (TREE_OPERAND (node, 0)) == ADDR_EXPR)
> -         node = TREE_OPERAND (TREE_OPERAND (node, 0), 0);
> -       goto done;
> -
> -     default:
> -       goto done;
> -     }
> -      node = TREE_OPERAND (node, 0);
> -    }
> - done:
> -  return node;
> -
> -}
> -
> -/* Compute the index of the NODE in its immediate container.
> -   NODE should be a FIELD_DECL (i.e. of struct or union), or an ARRAY_REF. */
> -static int
> -bpf_core_get_index (const tree node)
> -{
> -  enum tree_code code = TREE_CODE (node);
> -
> -  if (code == FIELD_DECL)
> -    {
> -      /* Lookup the index from the BTF information.  Some struct/union 
> members
> -      may not be emitted in BTF; only the BTF container has enough
> -      information to compute the correct index.  */
> -      int idx = bpf_core_get_sou_member_index (ctf_get_tu_ctfc (), node);
> -      if (idx >= 0)
> -     return idx;
> -    }
> -
> -  else if (code == ARRAY_REF || code == ARRAY_RANGE_REF || code == MEM_REF)
> -    {
> -      /* For array accesses, the index is operand 1.  */
> -      tree index = TREE_OPERAND (node, 1);
> -
> -      /* If the indexing operand is a constant, extracting is trivial.  */
> -      if (TREE_CODE (index) == INTEGER_CST && tree_fits_shwi_p (index))
> -     return tree_to_shwi (index);
> -    }
> -
> -  return -1;
> -}
> -
> -/* Synthesize a new builtin function declaration with signature TYPE.
> -   Used by bpf_resolve_overloaded_builtin to resolve calls to
> -   __builtin_preserve_access_index.  */
> -
> -static tree
> -bpf_core_newdecl (tree type, enum bpf_builtins which)
> -{
> -  tree rettype;
> -  char name[80];
> -  static unsigned long pai_count = 0;
> -  static unsigned long pfi_count = 0;
> -
> -  switch (which)
> -    {
> -    case BPF_BUILTIN_PRESERVE_ACCESS_INDEX:
> -      {
> -     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++);
> -      }
> -      break;
> -
> -    case BPF_BUILTIN_PRESERVE_FIELD_INFO:
> -      {
> -     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++);
> -      }
> -      break;
> -
> -    default:
> -      gcc_unreachable ();
> -    }
> -
> -  return add_builtin_function_ext_scope (name, rettype, -which,
> -                                      BUILT_IN_MD, NULL, NULL_TREE);
> -}
> -
> -/* Return whether EXPR could access some aggregate data structure that
> -   BPF CO-RE support needs to know about.  */
> -
> -static bool
> -bpf_core_is_maybe_aggregate_access (tree expr)
> -{
> -  switch (TREE_CODE (expr))
> -    {
> -    case COMPONENT_REF:
> -    case BIT_FIELD_REF:
> -    case ARRAY_REF:
> -    case ARRAY_RANGE_REF:
> -      return true;
> -    case ADDR_EXPR:
> -    case NOP_EXPR:
> -      return bpf_core_is_maybe_aggregate_access (TREE_OPERAND (expr, 0));
> -    default:
> -      return false;
> -    }
> -}
> -
> -struct core_walk_data {
> -  location_t loc;
> -  enum bpf_builtins which;
> -  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)
> -{
> -  struct core_walk_data *dat = (struct core_walk_data *) data;
> -
> -  /* If this is a type, don't do anything. */
> -  if (TYPE_P (*tp))
> -    {
> -      *walk_subtrees = 0;
> -      return NULL_TREE;
> -    }
> -
> -  /* Build a new function call to a type-resolved temporary builtin for the
> -     desired operation, and pass along args as necessary.  */
> -  tree newdecl = bpf_core_newdecl (TREE_TYPE (*tp), dat->which);
> -
> -  if (dat->which == BPF_BUILTIN_PRESERVE_ACCESS_INDEX)
> -    {
> -      if (bpf_core_is_maybe_aggregate_access (*tp))
> -     {
> -       *tp = build_call_expr_loc (dat->loc, newdecl, 1, *tp);
> -       *walk_subtrees = 0;
> -     }
> -    }
> -  else
> -    {
> -      *tp = build_call_expr_loc (dat->loc, newdecl, 2, *tp, dat->arg);
> -      *walk_subtrees = 0;
> -    }
> -
> -  return NULL_TREE;
> -}
> -
>  /* Implement target hook small_register_classes_for_mode_p.  */
>  
>  static bool
> @@ -1603,277 +1166,6 @@ bpf_small_register_classes_for_mode_p (machine_mode 
> mode)
>  #define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P \
>    bpf_small_register_classes_for_mode_p
>  
> -/* Return whether EXPR is a valid first argument for a call to
> -   __builtin_preserve_field_info.  */
> -
> -static bool
> -bpf_is_valid_preserve_field_info_arg (tree expr)
> -{
> -  switch (TREE_CODE (expr))
> -    {
> -    case COMPONENT_REF:
> -    case BIT_FIELD_REF:
> -    case ARRAY_REF:
> -    case ARRAY_RANGE_REF:
> -      return true;
> -    case NOP_EXPR:
> -      return bpf_is_valid_preserve_field_info_arg (TREE_OPERAND (expr, 0));
> -    case ADDR_EXPR:
> -      /* Do not accept ADDR_EXPRs like &foo.bar, but do accept accesses like
> -      foo.baz where baz is an array.  */
> -      return (TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == ARRAY_TYPE);
> -    default:
> -      return false;
> -    }
> -}
> -
> -/* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN (see gccint manual section
> -   Target Macros::Misc.).
> -   Used for CO-RE support builtins such as __builtin_preserve_access_index
> -   and __builtin_preserve_field_info.
> -
> -   FNDECL is the declaration of the builtin, and ARGLIST is the list of
> -   arguments passed to it, and is really a vec<tree,_> *.  */
> -
> -static tree
> -bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
> -{
> -  enum bpf_builtins which = (enum bpf_builtins) DECL_MD_FUNCTION_CODE 
> (fndecl);
> -
> -  if (which < BPF_BUILTIN_PRESERVE_ACCESS_INDEX
> -      || which >= BPF_BUILTIN_MAX)
> -    return NULL_TREE;
> -
> -  vec<tree, va_gc> *params = static_cast<vec<tree, va_gc> *> (arglist);
> -  unsigned n_params = params ? params->length() : 0;
> -
> -  if (!(which == BPF_BUILTIN_PRESERVE_ACCESS_INDEX && n_params == 1)
> -      && n_params != 2)
> -    {
> -      error_at (loc, "wrong number of arguments");
> -      return error_mark_node;
> -    }
> -
> -  tree param = (*params)[0];
> -
> -  /* If not generating BPF_CORE information, preserve_access_index does
> -     nothing, and simply "resolves to" the argument.  */
> -  if (which == BPF_BUILTIN_PRESERVE_ACCESS_INDEX && !TARGET_BPF_CORE)
> -    return param;
> -
> -  /* For __builtin_preserve_field_info, enforce that the parameter is 
> exactly a
> -     field access and not a more complex expression.  */
> -  else if (which == BPF_BUILTIN_PRESERVE_FIELD_INFO
> -        && !bpf_is_valid_preserve_field_info_arg (param))
> -    {
> -      error_at (EXPR_LOC_OR_LOC (param, loc),
> -             "argument is not a field access");
> -      return error_mark_node;
> -    }
> -
> -  /* Do remove_c_maybe_const_expr for the arg.
> -     TODO: WHY do we have to do this here? Why doesn't c-typeck take care
> -     of it before or after this hook? */
> -  if (TREE_CODE (param) == C_MAYBE_CONST_EXPR)
> -    param = C_MAYBE_CONST_EXPR_EXPR (param);
> -
> -  /* Construct a new function declaration with the correct type, and return
> -     a call to it.
> -
> -     Calls with statement-expressions, for example:
> -     _(({ foo->a = 1; foo->u[2].b = 2; }))
> -     require special handling.
> -
> -     We rearrange this into a new block scope in which each statement
> -     becomes a unique builtin call:
> -     {
> -       _ ({ foo->a = 1;});
> -       _ ({ foo->u[2].b = 2;});
> -     }
> -
> -     This ensures that all the relevant information remains within the
> -     expression trees the builtin finally gets.  */
> -
> -  struct core_walk_data data;
> -  data.loc = loc;
> -  data.which = which;
> -  if (which == BPF_BUILTIN_PRESERVE_ACCESS_INDEX)
> -    data.arg = NULL_TREE;
> -  else
> -    data.arg = (*params)[1];
> -
> -  walk_tree (&param, bpf_core_walk, (void *) &data, NULL);
> -
> -  return param;
> -}
> -
> -#undef TARGET_RESOLVE_OVERLOADED_BUILTIN
> -#define TARGET_RESOLVE_OVERLOADED_BUILTIN bpf_resolve_overloaded_builtin
> -
> -
> -/* Handling for __attribute__((preserve_access_index)) for BPF CO-RE support.
> -
> -   This attribute marks a structure/union/array type as "preseve", so that
> -   every access to that type should be recorded and replayed by the BPF 
> loader;
> -   this is just the same functionality as __builtin_preserve_access_index,
> -   but in the form of an attribute for an entire aggregate type.
> -
> -   Note also that nested structs behave as though they all have the 
> attribute.
> -   For example:
> -     struct X { int a; };
> -     struct Y { struct X bar} __attribute__((preserve_access_index));
> -     struct Y foo;
> -     foo.bar.a;
> -   will record access all the way to 'a', even though struct X does not have
> -   the preserve_access_index attribute.
> -
> -   This is to follow LLVM behavior.
> -
> -   This pass finds all accesses to objects of types marked with the 
> attribute,
> -   and wraps them in the same "low-level" builtins used by the builtin 
> version.
> -   All logic afterwards is therefore identical to the builtin version of
> -   preserve_access_index.  */
> -
> -/* True iff tree T accesses any member of a struct/union/class which is 
> marked
> -   with the PRESERVE_ACCESS_INDEX attribute.  */
> -
> -static bool
> -is_attr_preserve_access (tree t)
> -{
> -  if (t == NULL_TREE)
> -    return false;
> -
> -  poly_int64 bitsize, bitpos;
> -  tree var_off;
> -  machine_mode mode;
> -  int sign, reverse, vol;
> -
> -  tree base = get_inner_reference (t, &bitsize, &bitpos, &var_off, &mode,
> -                                &sign, &reverse, &vol);
> -
> -  if (TREE_CODE (base) == MEM_REF)
> -    {
> -      return lookup_attribute ("preserve_access_index",
> -                            TYPE_ATTRIBUTES (TREE_TYPE (base)));
> -    }
> -
> -  if (TREE_CODE (t) == COMPONENT_REF)
> -    {
> -      /* preserve_access_index propegates into nested structures,
> -      so check whether this is a component of another component
> -      which in turn is part of such a struct.  */
> -
> -      const tree op = TREE_OPERAND (t, 0);
> -
> -      if (TREE_CODE (op) == COMPONENT_REF)
> -     return is_attr_preserve_access (op);
> -
> -      const tree container = DECL_CONTEXT (TREE_OPERAND (t, 1));
> -
> -      return lookup_attribute ("preserve_access_index",
> -                            TYPE_ATTRIBUTES (container));
> -    }
> -
> -  else if (TREE_CODE (t) == ADDR_EXPR)
> -    return is_attr_preserve_access (TREE_OPERAND (t, 0));
> -
> -  return false;
> -}
> -
> -/* The body of pass_bpf_core_attr. Scan RTL for accesses to structs/unions
> -   marked with __attribute__((preserve_access_index)) and generate a CO-RE
> -   relocation for any such access.  */
> -
> -static void
> -handle_attr_preserve (function *fn)
> -{
> -  basic_block bb;
> -  rtx_insn *insn;
> -  FOR_EACH_BB_FN (bb, fn)
> -    {
> -      FOR_BB_INSNS (bb, insn)
> -     {
> -       if (!NONJUMP_INSN_P (insn))
> -         continue;
> -       rtx pat = PATTERN (insn);
> -       if (GET_CODE (pat) != SET)
> -         continue;
> -
> -       start_sequence();
> -
> -       for (int i = 0; i < 2; i++)
> -         {
> -           rtx mem = XEXP (pat, i);
> -           if (MEM_P (mem))
> -             {
> -               tree expr = MEM_EXPR (mem);
> -               if (!expr)
> -                 continue;
> -
> -               if (TREE_CODE (expr) == MEM_REF
> -                   && TREE_CODE (TREE_OPERAND (expr, 0)) == SSA_NAME)
> -                 {
> -                   gimple *def_stmt = SSA_NAME_DEF_STMT (TREE_OPERAND (expr, 
> 0));
> -                   if (def_stmt && is_gimple_assign (def_stmt))
> -                     expr = gimple_assign_rhs1 (def_stmt);
> -                 }
> -
> -               if (is_attr_preserve_access (expr))
> -                 maybe_make_core_relo (expr, BPF_RELO_FIELD_BYTE_OFFSET);
> -             }
> -         }
> -       rtx_insn *seq = get_insns ();
> -       end_sequence ();
> -       emit_insn_before (seq, insn);
> -     }
> -    }
> -}
> -
> -/* This pass finds accesses to structures marked with the BPF target 
> attribute
> -   __attribute__((preserve_access_index)). For every such access, a CO-RE
> -   relocation record is generated, to be output in the .BTF.ext section.  */
> -
> -namespace {
> -
> -const pass_data pass_data_bpf_core_attr =
> -{
> -  RTL_PASS, /* type */
> -  "bpf_core_attr", /* name */
> -  OPTGROUP_NONE, /* optinfo_flags */
> -  TV_NONE, /* tv_id */
> -  0, /* properties_required */
> -  0, /* properties_provided */
> -  0, /* properties_destroyed */
> -  0, /* todo_flags_start */
> -  0, /* todo_flags_finish */
> -};
> -
> -class pass_bpf_core_attr : public rtl_opt_pass
> -{
> -public:
> -  pass_bpf_core_attr (gcc::context *ctxt)
> -    : rtl_opt_pass (pass_data_bpf_core_attr, ctxt)
> -  {}
> -
> -  virtual bool gate (function *) { return TARGET_BPF_CORE; }
> -  virtual unsigned int execute (function *);
> -};
> -
> -unsigned int
> -pass_bpf_core_attr::execute (function *fn)
> -{
> -  handle_attr_preserve (fn);
> -  return 0;
> -}
> -
> -} /* Anonymous namespace.  */
> -
> -rtl_opt_pass *
> -make_pass_bpf_core_attr (gcc::context *ctxt)
> -{
> -  return new pass_bpf_core_attr (ctxt);
> -}
> -
>  /* Finally, build the GCC target.  */
>  
>  struct gcc_target targetm = TARGET_INITIALIZER;
> diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md
> index a69a239b9d6a..e9c00e445af3 100644
> --- a/gcc/config/bpf/bpf.md
> +++ b/gcc/config/bpf/bpf.md
> @@ -45,6 +45,7 @@
>    UNSPEC_AFXOR
>    UNSPEC_AXCHG
>    UNSPEC_ACMP
> +  UNSPEC_CORE_RELOC
>  ])
>  
>  ;;;; Constants
> @@ -367,6 +368,8 @@
>          ""
>          "
>  {
> +  bpf_replace_core_move_operands (operands);
> +
>    if (!register_operand(operands[0], <MM:MODE>mode)
>        && !register_operand(operands[1], <MM:MODE>mode))
>      operands[1] = force_reg (<MM:MODE>mode, operands[1]);
> @@ -384,6 +387,20 @@
>     {st<mop>\t%0,%1|*(<smop> *) (%0) = %1}"
>  [(set_attr "type" "ldx,alu,alu,stx,st")])
>  
> +(define_insn "mov_reloc_core<MM:mode>"
> +  [(set (match_operand:MM 0 "nonimmediate_operand" "=r,q,r")
> +     (unspec:MM [
> +       (match_operand:MM 1 "immediate_operand"  " I,I,B")
> +       (match_operand:SI 2 "immediate_operand"  " I,I,I")
> +      ] UNSPEC_CORE_RELOC)
> +   )]
> +  ""
> +  "@
> +   *return bpf_add_core_reloc (operands, \"{mov\t%0,%1|%0 = %1}\");
> +   *return bpf_add_core_reloc (operands, \"{st<mop>\t%0,%1|*(<smop> *) (%0) 
> = %1}\");
> +   *return bpf_add_core_reloc (operands, \"{lddw\t%0,%1|%0 = %1 ll}\");"
> +  [(set_attr "type" "alu,st,alu")])
> +
>  ;;;; Shifts
>  
>  (define_mode_iterator SIM [(SI "bpf_has_alu32") DI])
> diff --git a/gcc/config/bpf/core-builtins.cc b/gcc/config/bpf/core-builtins.cc
> new file mode 100644
> index 000000000000..575e63d8ea77
> --- /dev/null
> +++ b/gcc/config/bpf/core-builtins.cc
> @@ -0,0 +1,1394 @@
> +/* Subroutines used for code generation for eBPF.
> +   Copyright (C) 2019-2023 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 3, or (at your option)
> +any later version.
> +
> +GCC is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#define IN_TARGET_CODE 1
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tm.h"
> +#include "rtl.h"
> +#include "regs.h"
> +#include "insn-config.h"
> +#include "insn-attr.h"
> +#include "recog.h"
> +#include "output.h"
> +#include "alias.h"
> +#include "tree.h"
> +#include "stringpool.h"
> +#include "attribs.h"
> +#include "varasm.h"
> +#include "stor-layout.h"
> +#include "calls.h"
> +#include "function.h"
> +#include "explow.h"
> +#include "memmodel.h"
> +#include "emit-rtl.h"
> +#include "reload.h"
> +#include "tm_p.h"
> +#include "target.h"
> +#include "basic-block.h"
> +#include "expr.h"
> +#include "optabs.h"
> +#include "bitmap.h"
> +#include "df.h"
> +#include "c-family/c-common.h"
> +#include "diagnostic.h"
> +#include "builtins.h"
> +#include "predict.h"
> +#include "langhooks.h"
> +#include "flags.h"
> +
> +#include "cfg.h"
> +#include "gimple.h"
> +#include "gimple-iterator.h"
> +#include "gimple-walk.h"
> +#include "tree-pass.h"
> +#include "tree-iterator.h"
> +
> +#include "context.h"
> +#include "pass_manager.h"
> +
> +#include "gimplify.h"
> +#include "gimplify-me.h"
> +
> +#include "plugin.h"
> +
> +#include "ctfc.h"
> +#include "btf.h"
> +#include "coreout.h"
> +#include "core-builtins.h"
> +
> +/*
> + * BPF CO-RE builtins definition.
> +
> +   The expansion of CO-RE builtins occur in three steps:
> +   1. - bpf_resolve_overloaded_core_builtin (pack step)
> +     Right after the front-end, all of the CO-RE builtins are converted to an
> +     internal builtin __builtin_core_reloc, which takes a single argument and
> +     has polymorphic return value to fit the particular expected return type
> +     from the original builtin.  The first argument contains an index 
> argument
> +     which points to the information stored in a vec<struct cr_builtins>
> +     which collects the required information from the original CO-RE builtin 
> in
> +     order to use it later on in the __builtin_core_reloc expansion (the next
> +     step).
> +
> +   2. - bpf_expand_core_builtin
> +     In this step, the __builtin_core_reloc is expanded to a 
> unspec:UNSPEC_CORE_RELOC
> +     with 3 operands, destination, source and the index. The index operand
> +     is the index in the vec constructed in the previous step.
> +
> +   3. - final asm output (process step)
> +     This is the output of the unspec:UNSPEC_CORE_RELOC. The index passed in
> +     the third operand is read and extracted as a integer from the rtx node.
> +     The data is collected from the vec and it is used to create
> +     the proper CO-RE relocation as well as do the final assembly output.
> +     It also creates a label to mark the location of the move instruction 
> that
> +     is used in the CO-RE relocation.
> +
> +  The initialization of the CO-RE builtins infrastructure occurs in
> +  bpf_is function.  It creates a struct
> +  builtin_helpers_t arrays which defines the kind argument position,
> +  the callback helpers, kind, compare, pack and process, for each individual
> +  type of builtin argument possible in the original CO-RE builtins.
> +
> +  More precisely, field expression, type and enum value, used in the 
> following
> +  relocations:
> +    - __builtin_core_preserve_access_index (<field_expr>)
> +    - __builtin_core_field_info (<field_expr>, <kind>)
> +    - __builtin_core_type_id (<type>, <kind>)
> +    - __builtin_core_type_info (<type>, <kind>)
> +    - __builtin_core_enum_value (<enum_value>, <kind>)
> +
> +  The kind helper allows to identify the proper relocation for the builtin
> +  call based on the value within the kind argument.
> +
> +  The compare helper is used to identify if a new builtin call has similar
> +  arguments to any other builtin call with the compiling unit.
> +  This enables the possibility to optimize consecutive similar calls of the
> +  builtins.
> +
> +  The pack helper callbacks are suppose to decode the original CO-RE builtin
> +  call arguments, verify that it is a valid tree node for the particular
> +  builtin, allocate a struct cr_local in vector and write it with the
> +  relevant data for the particular builtin type.
> +
> +  The process helper should take the data constructed in the pack helper and
> +  create a struct cr_final element which contains the essential
> +  information to create a CO-RE relocation.
> +  This information is further used by the final assembly output step to 
> define
> +  the CO-RE relocation and pass-through the default value for the original
> +  CO-RE builtin.
> +
> +
> +  BPF CO-RE preserve access is supported in two forms:
> +  - A target builtin, __builtin_preserve_access_index
> +
> +    This builtin accepts a single argument.  Any access to an aggregate data
> +    structure (struct, union or array) within the argument will be recorded 
> by
> +    the CO-RE machinery, resulting in a relocation record being placed in the
> +    .BTF.ext section of the output.
> +
> +    It is implemented in bpf_resolve_overloaded_builtin () and
> +    bpf_expand_builtin (), using the supporting routines below.
> +
> +  - An attribute, __attribute__((preserve_access_index))
> +
> +    This attribute can be applied to struct and union types.  Any access to a
> +    type with this attribute will be recorded by the CO-RE machinery.
> +    In the expand, any move matching is checked if any of its operands is
> +    an expression to an attributed type, and if so, the expand will emit a
> +    unspec:UNSPEC_CORE_RELOC that later on, in final assembly output, will
> +    create the CO-RE relocation, just like it would happen if it was defined
> +    as a builtin.  */
> +
> +
> +struct cr_builtins
> +{
> +  tree type;
> +  tree expr;
> +  tree default_value;
> +  rtx rtx_default_value;
> +  enum btf_core_reloc_kind kind; /* Recovered from proper argument.  */
> +  enum bpf_builtins orig_builtin_code;
> +  tree orig_arg_expr;
> +};
> +
> +#define CORE_BUILTINS_DATA_EMPTY \
> +  { NULL_TREE, NULL_TREE, NULL_TREE, NULL_RTX, BPF_RELO_INVALID, \
> +    BPF_BUILTIN_UNUSED, NULL }
> +
> +/* Vector definition and its access function.  */
> +vec<struct cr_builtins> builtins_data;
> +
> +static inline int
> +allocate_builtin_data ()
> +{
> +  struct cr_builtins data = CORE_BUILTINS_DATA_EMPTY;
> +  int ret = builtins_data.length ();
> +  builtins_data.safe_push (data);
> +  return ret;
> +}
> +
> +static inline struct cr_builtins *
> +get_builtin_data (int index)
> +{
> +  return &builtins_data[index];
> +}
> +
> +typedef bool
> +(*builtin_local_data_compare_fn) (struct cr_builtins *a,
> +                               struct cr_builtins *b);
> +static inline int
> +search_builtin_data (builtin_local_data_compare_fn callback,
> +                  struct cr_builtins *elem)
> +{
> +  unsigned int i;
> +  for (i = 0; i < builtins_data.length (); i++)
> +    if ((callback != NULL && (callback) (elem, &builtins_data[i]))
> +       || (callback == NULL
> +        && (builtins_data[i].orig_arg_expr == elem->orig_arg_expr)))
> +      return (int) i;
> +
> +  return -1;
> +}
> +
> +/* Possible relocation decisions.  */
> +enum cr_decision
> +{
> +  FAILED_VALIDATION = 0,
> +  KEEP_ORIGINAL_NO_RELOCATION,
> +  REPLACE_CREATE_RELOCATION,
> +  REPLACE_NO_RELOCATION
> +};
> +
> +/* Core Relocation Pack local structure.  */
> +struct cr_local
> +{
> +  struct cr_builtins reloc_data;
> +  enum cr_decision reloc_decision;
> +  bool fail;
> +};
> +#define CR_LOCAL_EMPTY { CORE_BUILTINS_DATA_EMPTY, FAILED_VALIDATION, false }
> +
> +/* Core Relocation Final data */
> +struct cr_final
> +{
> +  char *str;
> +  tree type;
> +  enum btf_core_reloc_kind kind;
> +};
> +
> +/* CO-RE builtin helpers struct.  Used and initialized in
> +   bpf_init_core_builtins.  */
> +struct builtin_helpers
> +{
> +  enum btf_core_reloc_kind (*kind) (tree *args, int nargs);
> +  bool (*compare) (struct cr_builtins *a, struct cr_builtins *b);
> +  struct cr_local (*pack) (tree *args,
> +                        enum btf_core_reloc_kind kind,
> +                        enum bpf_builtins code);
> +  struct cr_final (*process) (struct cr_builtins *data);
> +  bool is_pure;
> +  bool is_valid;
> +};
> +
> +struct builtin_helpers
> +  core_builtin_helpers[(int) BPF_BUILTIN_MAX];
> +
> +#define BPF_CORE_HELPER_NOTSET { NULL, NULL, NULL, NULL, false, false }
> +#define BPF_CORE_HELPER_SET(KIND, COMPARE, PACK, PROCESS, IS_PURE) \
> +     { KIND, COMPARE, PACK, PROCESS, IS_PURE, true }
> +
> +enum bpf_plugin_states
> +{
> +  BPF_PLUGIN_DISABLED = 0,
> +  BPF_PLUGIN_ENABLED,
> +  BPF_PLUGIN_REMOVED
> +};
> +enum bpf_plugin_states plugin_state = BPF_PLUGIN_DISABLED;
> +
> +static void
> +remove_parser_plugin ()
> +{
> +  /* Restore state of the plugin system.  */
> +  if (flag_plugin_added == true && plugin_state != BPF_PLUGIN_REMOVED)
> +    {
> +      unregister_callback ("bpf_collect_enum_info", PLUGIN_FINISH_TYPE);
> +      flag_plugin_added = (bool) plugin_state == BPF_PLUGIN_ENABLED;
> +      plugin_state = BPF_PLUGIN_REMOVED;
> +    }
> +}
> +
> +#define bpf_error(MSG) { \
> +  remove_parser_plugin (); \
> +  error (MSG); \
> +}
> +
> +#define bpf_error_at(LOC, MSG) { \
> +  remove_parser_plugin (); \
> +  error_at (LOC, MSG); \
> +}
> +
> +
> +/* Helper compare functions used to verify if multiple builtin calls contain
> +   the same argument as input.  In that case the builtin calls can be 
> optimized
> +   out by identifying redundat calls.  This happen since the internal
> +   __core_reloc builtin is marked as PURE.  */
> +
> +static inline bool
> +compare_same_kind (struct cr_builtins *a, struct cr_builtins *b)
> +{
> +  return a->kind == b->kind;
> +}
> +static inline bool
> +compare_same_ptr_expr (struct cr_builtins *a, struct cr_builtins *b)
> +{
> +  return compare_same_kind (a, b) && a->expr == b->expr;
> +}
> +static inline bool
> +compare_same_ptr_type (struct cr_builtins *a, struct cr_builtins *b)
> +{
> +  return compare_same_kind (a, b) && a->type == b->type;
> +}
> +
> +/* Handling for __attribute__((preserve_access_index)) for BPF CO-RE support.
> +
> +   This attribute marks a structure/union/array type as "preseve", so that
> +   every access to that type should be recorded and replayed by the BPF 
> loader;
> +   this is just the same functionality as __builtin_preserve_access_index,
> +   but in the form of an attribute for an entire aggregate type.
> +
> +   Note also that nested structs behave as though they all have the 
> attribute.
> +   For example:
> +     struct X { int a; };
> +     struct Y { struct X bar} __attribute__((preserve_access_index));
> +     struct Y foo;
> +     foo.bar.a;
> +   will record access all the way to 'a', even though struct X does not have
> +   the preserve_access_index attribute.
> +
> +   This is to follow LLVM behavior. */
> +
> +/* True if tree T accesses any member of a struct/union/class which is marked
> +   with the PRESERVE_ACCESS_INDEX attribute.  */
> +
> +static bool
> +is_attr_preserve_access (tree t)
> +{
> +  if (t == NULL_TREE)
> +    return false;
> +
> +  poly_int64 bitsize, bitpos;
> +  tree var_off;
> +  machine_mode mode;
> +  int sign, reverse, vol;
> +
> +  tree base = get_inner_reference (t, &bitsize, &bitpos, &var_off, &mode,
> +                                &sign, &reverse, &vol);
> +
> +  if (TREE_CODE (base) == MEM_REF)
> +    {
> +      return lookup_attribute ("preserve_access_index",
> +                            TYPE_ATTRIBUTES (TREE_TYPE (base)));
> +    }
> +
> +  if (TREE_CODE (t) == COMPONENT_REF)
> +    {
> +      /* preserve_access_index propagates into nested structures,
> +      so check whether this is a component of another component
> +      which in turn is part of such a struct.  */
> +
> +      const tree op = TREE_OPERAND (t, 0);
> +
> +      if (TREE_CODE (op) == COMPONENT_REF)
> +     return is_attr_preserve_access (op);
> +
> +      const tree container = DECL_CONTEXT (TREE_OPERAND (t, 1));
> +
> +      return lookup_attribute ("preserve_access_index",
> +                            TYPE_ATTRIBUTES (container));
> +    }
> +
> +  else if (TREE_CODE (t) == ADDR_EXPR)
> +    return is_attr_preserve_access (TREE_OPERAND (t, 0));
> +
> +  return false;
> +}
> +
> +
> +/* 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
> +core_field_info (tree src, enum btf_core_reloc_kind kind)
> +{
> +  unsigned int result;
> +  poly_int64 bitsize, bitpos;
> +  tree var_off = NULL_TREE;
> +  machine_mode mode;
> +  int unsignedp, reversep, volatilep;
> +  location_t loc = EXPR_LOCATION (src);
> +  tree type = TREE_TYPE (src);
> +
> +  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:
> +      {
> +     type = unsigned_type_node;
> +     if (var_off != NULL_TREE)
> +       {
> +         bpf_error_at (loc, "unsupported variable field offset");
> +         return error_mark_node;
> +       }
> +
> +     if (bitfieldp)
> +       result = start_bitpos / 8;
> +     else
> +       result = bitpos / 8;
> +      }
> +      break;
> +
> +    case BPF_RELO_FIELD_BYTE_SIZE:
> +      {
> +     type = unsigned_type_node;
> +     if (mode == BLKmode && bitsize == -1)
> +       {
> +         bpf_error_at (loc, "unsupported variable size field access");
> +         return error_mark_node;
> +       }
> +
> +     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))
> +           {
> +             bpf_error_at (loc, "unsupported field expression");
> +             return error_mark_node;
> +           }
> +         result = result / 8;
> +       }
> +     else
> +       result = bitsize / 8;
> +      }
> +      break;
> +
> +    case BPF_RELO_FIELD_EXISTS:
> +      type = unsigned_type_node;
> +      /* The field always exists at compile time.  */
> +      result = 1;
> +      break;
> +
> +    case BPF_RELO_FIELD_SIGNED:
> +      type = unsigned_type_node;
> +      result = !unsignedp;
> +      break;
> +
> +    case BPF_RELO_FIELD_LSHIFT_U64:
> +    case BPF_RELO_FIELD_RSHIFT_U64:
> +      {
> +     type = unsigned_type_node;
> +     if (mode == BLKmode && bitsize == -1)
> +       {
> +         bpf_error_at (loc, "unsupported variable size field access");
> +         return error_mark_node;
> +       }
> +     if (var_off != NULL_TREE)
> +       {
> +         bpf_error_at (loc, "unsupported variable field offset");
> +         return error_mark_node;
> +       }
> +
> +     if (!bitfieldp)
> +       {
> +         if (bitsize > 64)
> +           {
> +             bpf_error_at (loc, "field size too large");
> +             return error_mark_node;
> +           }
> +         result = 64 - bitsize;
> +         break;
> +       }
> +
> +     if (end_bitpos - start_bitpos > 64)
> +       {
> +         bpf_error_at (loc, "field size too large");
> +         return error_mark_node;
> +       }
> +
> +     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:
> +      bpf_error ("invalid second argument to built-in function");
> +      return error_mark_node;
> +      break;
> +    }
> +
> +  return build_int_cst (type, result);
> +}
> +
> +/* Compute the index of the NODE in its immediate container.
> +   NODE should be a FIELD_DECL (i.e. of struct or union), or an ARRAY_REF.  
> */
> +
> +static int
> +bpf_core_get_index (const tree node)
> +{
> +  enum tree_code code = TREE_CODE (node);
> +
> +  if (code == FIELD_DECL)
> +    {
> +      /* Lookup the index from the type fields information.  */
> +      const tree container = DECL_CONTEXT (node);
> +      int i = 0;
> +      for (tree l = TYPE_FIELDS (container); l; l = DECL_CHAIN (l))
> +     {
> +       if (l == node)
> +         return i;
> +       i++;
> +     }
> +    }
> +  else if (code == ARRAY_REF || code == ARRAY_RANGE_REF || code == MEM_REF)
> +    {
> +      /* For array accesses, the index is operand 1.  */
> +      tree index = TREE_OPERAND (node, 1);
> +
> +      /* If the indexing operand is a constant, extracting is trivial.  */
> +      if (TREE_CODE (index) == INTEGER_CST && tree_fits_shwi_p (index))
> +     return tree_to_shwi (index);
> +    }
> +  else if (code == POINTER_PLUS_EXPR)
> +    {
> +      tree offset = TREE_OPERAND (node, 1);
> +      tree type = TREE_TYPE (TREE_OPERAND (node, 0));
> +
> +      if (TREE_CODE (offset) == INTEGER_CST && tree_fits_shwi_p (offset)
> +       && COMPLETE_TYPE_P (type) && tree_fits_shwi_p (TYPE_SIZE (type)))
> +     {
> +       HOST_WIDE_INT offset_i = tree_to_shwi (offset);
> +       HOST_WIDE_INT type_size_i = tree_to_shwi (TYPE_SIZE_UNIT (type));
> +       if ((offset_i % type_size_i) == 0)
> +         return offset_i / type_size_i;
> +     }
> +    }
> +
> +  gcc_unreachable ();
> +  return -1;
> +}
> +
> +/* This function takes a possible field expression (node) and verifies it is
> +   valid, extracts what should be the root of the valid field expression and
> +   composes the accessors array of indices.  The accessors are later used in 
> the
> +   CO-RE relocation in the string field.  */
> +
> +static unsigned char
> +compute_field_expr (tree node, unsigned int *accessors, bool *valid,
> +                 tree *root)
> +{
> +  unsigned char n = 0;
> +  if (node == NULL_TREE)
> +    {
> +      *valid = false;
> +      return 0;
> +    }
> +
> +  switch (TREE_CODE (node))
> +    {
> +    case INDIRECT_REF:
> +    case ADDR_EXPR:
> +      accessors[0] = 0;
> +      n = compute_field_expr (TREE_OPERAND (node, 0), &accessors[0], valid,
> +                           root);
> +      *root = node;
> +      return n + 1;
> +    case POINTER_PLUS_EXPR:
> +      accessors[0] = bpf_core_get_index (node);
> +      *root = node;
> +      return 1;
> +    case COMPONENT_REF:
> +      n = compute_field_expr (TREE_OPERAND (node, 0), accessors, valid,
> +                           root);
> +      accessors[n] = bpf_core_get_index (TREE_OPERAND (node, 1));
> +      *root = node;
> +      return n + 1;
> +    case ARRAY_REF:
> +    case ARRAY_RANGE_REF:
> +    case MEM_REF:
> +      n = compute_field_expr (TREE_OPERAND (node, 0), accessors, valid, 
> root);
> +      accessors[n] = bpf_core_get_index (node);
> +      *root = node;
> +      return n + 1;
> +    case NOP_EXPR:
> +      n = compute_field_expr (TREE_OPERAND (node, 0), accessors, valid, 
> root);
> +      *root = node;
> +      return n;
> +    case TARGET_EXPR:
> +      {
> +     tree value = TREE_OPERAND (node, 1);
> +     if (TREE_CODE (value) == BIND_EXPR
> +         && TREE_CODE (value = BIND_EXPR_BODY (value)) == MODIFY_EXPR)
> +       return compute_field_expr (TREE_OPERAND (value, 1), accessors, valid,
> +                                  root);
> +      }
> +      *root = node;
> +      return 0;
> +    case SSA_NAME:
> +    case VAR_DECL:
> +    case PARM_DECL:
> +      return 0;
> +    default:
> +      *valid = false;
> +      return 0;
> +    }
> +}
> +
> +static struct cr_local
> +pack_field_expr_for_access_index (tree *args,
> +                               enum btf_core_reloc_kind kind,
> +                               enum bpf_builtins code ATTRIBUTE_UNUSED)
> +{
> +  struct cr_local ret = CR_LOCAL_EMPTY;
> +  ret.fail = false;
> +
> +  tree arg = args[0];
> +  tree root = arg;
> +
> +  /* Avoid double-recording information if 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))
> +    {
> +      ret.reloc_decision = REPLACE_NO_RELOCATION;
> +      ret.reloc_data.expr = arg;
> +    }
> +  else
> +    {
> +      ret.reloc_decision = REPLACE_CREATE_RELOCATION;
> +
> +      unsigned int accessors[100];
> +      bool valid = true;
> +      compute_field_expr (arg, accessors, &valid, &root);
> +
> +      if (valid == true)
> +     ret.reloc_data.expr = root;
> +      else
> +     {
> +       bpf_error_at (EXPR_LOC_OR_LOC (arg, UNKNOWN_LOCATION),
> +                     "Cannot compute index for field argument");
> +       ret.fail = true;
> +     }
> +    }
> +
> +  /* Note: the type of default_value is used to define the return type of
> +   __builtin_core_reloc in bpf_resolve_overloaded_core_builtin.  */
> +  ret.reloc_data.type = TREE_TYPE (root);
> +  ret.reloc_data.default_value = build_int_cst (ret.reloc_data.type, 0);
> +  ret.reloc_data.kind = kind;
> +
> +  if (TREE_CODE (ret.reloc_data.default_value) == ERROR_MARK)
> +    ret.fail = true;
> +
> +  return ret;
> +}
> +
> +static struct cr_local
> +pack_field_expr_for_preserve_field (tree *args,
> +                                 enum btf_core_reloc_kind kind,
> +                                 enum bpf_builtins code ATTRIBUTE_UNUSED)
> +{
> +  struct cr_local ret = CR_LOCAL_EMPTY;
> +  ret.fail = false;
> +
> +  tree arg = args[0];
> +  tree tmp;
> +  tree root = arg;
> +
> +  /* Remove cast to void * created by front-end to fit builtin type, when 
> passed
> +   * a simple expression like f->u.  */
> +  if (TREE_CODE (arg) == NOP_EXPR && (tmp = TREE_OPERAND (arg, 0))
> +      && TREE_CODE (tmp) == ADDR_EXPR && (tmp = TREE_OPERAND (tmp, 0))
> +      && arg != NULL_TREE)
> +    arg = tmp;
> +
> +  unsigned int accessors[100];
> +  bool valid = true;
> +  compute_field_expr (arg, accessors, &valid, &root);
> +
> +  if (valid == true)
> +    ret.reloc_data.expr = root;
> +  else
> +    {
> +      bpf_error_at (EXPR_LOC_OR_LOC (arg, UNKNOWN_LOCATION),
> +                 "argument is not a field access");
> +      ret.fail = true;
> +    }
> +
> +  ret.reloc_decision = REPLACE_CREATE_RELOCATION;
> +  ret.reloc_data.type = TREE_TYPE (root);
> +  ret.reloc_data.default_value = core_field_info (root, kind);
> +  ret.reloc_data.kind = kind;
> +
> +  if (TREE_CODE (ret.reloc_data.default_value) == ERROR_MARK)
> +    ret.fail = true;
> +
> +  return ret;
> +}
> +
> +static struct cr_final
> +process_field_expr (struct cr_builtins *data)
> +{
> +  gcc_assert (data->kind == BPF_RELO_FIELD_BYTE_OFFSET
> +           || data->kind == BPF_RELO_FIELD_BYTE_SIZE
> +           || data->kind == BPF_RELO_FIELD_LSHIFT_U64
> +           || data->kind == BPF_RELO_FIELD_RSHIFT_U64
> +           || data->kind == BPF_RELO_FIELD_SIGNED
> +           || data->kind == BPF_RELO_FIELD_EXISTS);
> +
> +  unsigned int accessors[100];
> +  unsigned char nr_accessors = 0;
> +  bool valid = true;
> +  tree root = NULL_TREE;
> +  tree expr = data->expr;
> +  tree type = TREE_TYPE (data->expr);
> +
> +  if (TREE_CODE (expr) == ADDR_EXPR)
> +    expr = TREE_OPERAND (expr, 0);
> +
> +  nr_accessors = compute_field_expr (expr, accessors, &valid, &root);
> +
> +  struct cr_final ret = { NULL, type, data->kind};
> +
> +  char str[100];
> +  if (nr_accessors > 0)
> +    {
> +      int n = 0;
> +      for (int i = 0; i < nr_accessors; i++)
> +     n += snprintf (str + n, sizeof (str) - n,
> +                    i == 0 ? "%u" : ":%u", accessors[i]);
> +      ret.str = CONST_CAST (char *, ggc_strdup (str));
> +    }
> +  else
> +    gcc_unreachable ();
> +
> +  return ret;
> +}
> +
> +hash_map <tree, tree> bpf_enum_mappings;
> +
> +tree enum_value_type = NULL_TREE;
> +static struct cr_local
> +pack_enum_value (tree *args, enum btf_core_reloc_kind kind,
> +              enum bpf_builtins code ATTRIBUTE_UNUSED)
> +{
> +  struct cr_local ret = CR_LOCAL_EMPTY;
> +  ret.reloc_decision = REPLACE_CREATE_RELOCATION;
> +  ret.fail = false;
> +
> +  tree *result = NULL;
> +  tree tmp = args[0];
> +  tree enum_value = args[1];
> +  tree type = NULL_TREE;
> +
> +  /* Deconstructing "*(typeof (enum_type) *) enum_value" to collect both the
> +   * enum_type and enum_value.  */
> +  if (TREE_CODE (tmp) != TARGET_EXPR
> +      || (type = TREE_TYPE (tmp)) == NULL_TREE
> +      || (TREE_CODE (type) != POINTER_TYPE)
> +      || (type = TREE_TYPE (type)) == NULL_TREE
> +      || (TREE_CODE (type) != ENUMERAL_TYPE))
> +    {
> +      bpf_error ("invalid type argument format for enum value builtin");
> +      ret.fail = true;
> +    }
> +
> +  if (TREE_CODE (enum_value) != INTEGER_CST)
> +    goto pack_enum_value_fail;
> +
> +  result = bpf_enum_mappings.get (enum_value);
> +  if (result == NULL)
> +    goto pack_enum_value_fail;
> +
> +  tmp = *result;
> +
> +  if (TREE_CODE (tmp) != CONST_DECL)
> +    {
> +pack_enum_value_fail:
> +      bpf_error ("invalid enum value argument for enum value builtin");
> +      ret.fail = true;
> +    }
> +  else
> +    {
> +      ret.reloc_data.expr = tmp;
> +      if (kind == BPF_RELO_ENUMVAL_VALUE)
> +     ret.reloc_data.default_value = enum_value;
> +      else
> +     ret.reloc_data.default_value = integer_one_node;
> +    }
> +
> +  ret.reloc_data.type = type;
> +  ret.reloc_data.kind = kind;
> +  return ret;
> +}
> +
> +static struct cr_final
> +process_enum_value (struct cr_builtins *data)
> +{
> +  gcc_assert (data->kind == BPF_RELO_ENUMVAL_EXISTS
> +           || data->kind == BPF_RELO_ENUMVAL_VALUE);
> +
> +  tree expr = data->expr;
> +  tree type = data->type;
> +
> +  struct cr_final ret = { NULL, type, data->kind };
> +
> +  if (TREE_CODE (expr) == CONST_DECL
> +     && TREE_CODE (type) == ENUMERAL_TYPE)
> +    {
> +      unsigned int index = 0;
> +      for (tree l = TYPE_VALUES (type); l; l = TREE_CHAIN (l))
> +     {
> +       if (TREE_VALUE (l) == expr)
> +         {
> +           ret.str = (char *) ggc_alloc_atomic ((index / 10) + 1);
> +           sprintf (ret.str, "%d", index);
> +           break;
> +         }
> +       index++;
> +     }
> +    }
> +  else
> +    gcc_unreachable ();
> +
> +  return ret;
> +}
> +
> +static struct cr_local
> +pack_type (tree *args, enum btf_core_reloc_kind kind,
> +        enum bpf_builtins code ATTRIBUTE_UNUSED)
> +{
> +  struct cr_local ret = CR_LOCAL_EMPTY;
> +  ret.reloc_decision = FAILED_VALIDATION;
> +  ret.reloc_data.default_value = integer_zero_node;
> +  ret.fail = false;
> +
> +  tree root_type = NULL_TREE;
> +  tree tmp = args[0];
> +  HOST_WIDE_INT type_size_i;
> +
> +  /* Typical structure to match:
> +   *    *({ extern typeof (TYPE) *<tmp_name>; <tmp_name>; })
> +   */
> +
> +  /* Extract Pointer dereference from the construct.  */
> +
> +  while (tmp != NULL_TREE
> +     && (TREE_CODE (tmp) == INDIRECT_REF
> +         || TREE_CODE (tmp) == NOP_EXPR))
> +    tmp = TREE_OPERAND (tmp, 0);
> +
> +  if (TREE_CODE (tmp) != TARGET_EXPR
> +      || TREE_CODE (tmp = TREE_OPERAND (tmp, 1)) != BIND_EXPR)
> +    goto pack_type_fail;
> +
> +  tmp = BIND_EXPR_VARS (tmp);
> +
> +  if (TREE_CODE (tmp) != TYPE_DECL
> +      && TREE_CODE (tmp) != VAR_DECL)
> +    goto pack_type_fail;
> +
> +  tmp = TREE_TYPE (tmp);
> +
> +  if (TREE_CODE (tmp) == POINTER_TYPE)
> +    tmp = TREE_TYPE (tmp);
> +
> +  root_type = tmp;
> +
> +  if (TREE_CODE (tmp) != RECORD_TYPE
> +      && TREE_CODE (tmp) != UNION_TYPE
> +      && TREE_CODE (tmp) != ENUMERAL_TYPE
> +      && (TREE_CODE (tmp) != POINTER_TYPE
> +       || TREE_CODE (TREE_TYPE (tmp)) == FUNCTION_TYPE)
> +      && (TREE_CODE (tmp) != POINTER_TYPE
> +       || TREE_CODE (TREE_TYPE (tmp)) == VOID_TYPE)
> +      && TREE_CODE (tmp) != ARRAY_TYPE
> +      && TREE_CODE (tmp) != INTEGER_TYPE)
> +    goto pack_type_fail;
> +
> +  ret.reloc_data.type = root_type;
> +  ret.reloc_decision = REPLACE_CREATE_RELOCATION;
> +
> +  /* Force this type to be marked as used in dwarf2out. */
> +  gcc_assert (cfun);
> +  if (cfun->used_types_hash == NULL)
> +    cfun->used_types_hash = hash_set<tree>::create_ggc (37);
> +  cfun->used_types_hash->add (root_type);
> +
> +  type_size_i = tree_to_shwi (TYPE_SIZE_UNIT (ret.reloc_data.type));
> +
> +  switch (kind)
> +    {
> +      case BPF_RELO_TYPE_SIZE:
> +     ret.reloc_data.default_value = build_int_cst (integer_type_node,
> +                                                   type_size_i);
> +     break;
> +      case BPF_RELO_TYPE_EXISTS:
> +      case BPF_RELO_TYPE_MATCHES:
> +     ret.reloc_data.default_value = integer_one_node;
> +     break;
> +      case BPF_RELO_TYPE_ID_LOCAL:
> +      case BPF_RELO_TYPE_ID_TARGET:
> +     ret.reloc_data.default_value = integer_zero_node;
> +     break;
> +      default:
> +     break;
> +    }
> +
> +  ret.reloc_data.kind = kind;
> +  return ret;
> +
> +pack_type_fail:
> +      bpf_error_at (EXPR_LOC_OR_LOC (args[0], UNKNOWN_LOCATION),
> +                 "invelid first argument format for enum value builtin");
> +      ret.fail = true;
> +  return ret;
> +}
> +
> +static struct cr_final
> +process_type (struct cr_builtins *data)
> +{
> +  gcc_assert (data->kind == BPF_RELO_TYPE_ID_LOCAL
> +           || data->kind == BPF_RELO_TYPE_ID_TARGET
> +           || data->kind == BPF_RELO_TYPE_EXISTS
> +           || data->kind == BPF_RELO_TYPE_SIZE
> +           || data->kind == BPF_RELO_TYPE_MATCHES);
> +
> +  struct cr_final ret;
> +  ret.str = NULL;
> +  ret.type = data->type;
> +  ret.kind = data->kind;
> +
> +  if ((data->kind == BPF_RELO_TYPE_ID_LOCAL
> +      || data->kind == BPF_RELO_TYPE_ID_TARGET)
> +      && data->default_value != NULL)
> +  {
> +    ctf_container_ref ctfc = ctf_get_tu_ctfc ();
> +    unsigned int btf_id = get_btf_id (ctf_lookup_tree_type (ctfc, ret.type));
> +    data->rtx_default_value = expand_normal (build_int_cst 
> (integer_type_node,
> +                                                         btf_id));
> +  }
> +
> +  return ret;
> +}
> +
> +static bool
> +bpf_require_core_support ()
> +{
> +  if (!TARGET_BPF_CORE)
> +    {
> +      bpf_error ("BPF CO-RE is required but not enabled");
> +      return false;
> +    }
> +  return true;
> +}
> +
> +/* BPF Compile Once - Run Everywhere (CO-RE) support.  Construct a CO-RE
> +   relocation record in DATA to be emitted in the .BTF.ext
> +   section.  Does nothing if we are not targetting BPF CO-RE, or if the
> +   constructed relocation would be a no-op.  */
> +
> +static void
> +make_core_relo (struct cr_final *data, rtx_code_label *label)
> +{
> +  /* If we are not targetting BPF CO-RE, do not make a relocation.  We
> +     might not be generating any debug info at all.  */
> +  if (!bpf_require_core_support ())
> +    return;
> +
> +  gcc_assert (data->type);
> +
> +  /* 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 (data->type, section_name, data->str, label,
> +                   data->kind);
> +}
> +
> +/* Support function to extract kind information for CO-RE builtin
> +   calls.  */
> +
> +static inline char
> +read_kind (tree kind, char max_value, char enum_offset)
> +{
> +  char kind_val = 0;
> +
> +  if (kind == NULL_TREE)
> +    goto invalid_kind_arg_error;
> +
> +  if (TREE_CODE (kind) != CONST_DECL
> +      && TREE_CODE (kind) == NOP_EXPR)
> +    kind = TREE_OPERAND (kind, 0);
> +
> +  if (TREE_CODE (kind) == CONST_DECL)
> +    kind = DECL_INITIAL (kind);
> +
> +  if (TREE_CODE (kind) == INTEGER_CST
> +      && tree_fits_uhwi_p (kind))
> +    kind_val = tree_to_uhwi (kind);
> +  else
> +    goto invalid_kind_arg_error;
> +
> +  if (kind_val > max_value)
> +    {
> +invalid_kind_arg_error:
> +      bpf_error ("invalid kind argument to core builtin");
> +      return -1;
> +    }
> +  return kind_val + enum_offset;
> +}
> +
> +#define KIND_EXPECT_NARGS(N, MSG) \
> +  { if (nargs != N) { bpf_error (MSG); return BPF_RELO_INVALID; } }
> +
> +/* Helper functions to extract kind information.  */
> +static inline enum btf_core_reloc_kind
> +kind_access_index (tree *args ATTRIBUTE_UNUSED, int nargs)
> +{
> +  KIND_EXPECT_NARGS (1,
> +     "wrong number of arguments for access index core builtin");
> +  return BPF_RELO_FIELD_BYTE_OFFSET;
> +}
> +static inline enum btf_core_reloc_kind
> +kind_preserve_field_info (tree *args, int nargs)
> +{
> +  KIND_EXPECT_NARGS (2,
> +     "wrong number of arguments for field info core builtin");
> +  return (enum btf_core_reloc_kind) read_kind (args[1], 5,
> +                                            BPF_RELO_FIELD_BYTE_OFFSET);
> +}
> +static inline enum btf_core_reloc_kind
> +kind_enum_value (tree *args, int nargs)
> +{
> +  KIND_EXPECT_NARGS (3,
> +     "wrong number of arguments for enum value core builtin");
> +  return (enum btf_core_reloc_kind) read_kind (args[2], 1,
> +                                            BPF_RELO_ENUMVAL_EXISTS);
> +}
> +static inline enum btf_core_reloc_kind
> +kind_type_id (tree *args, int nargs)
> +{
> +  KIND_EXPECT_NARGS (2,
> +     "wrong number of arguments for type id core builtin");
> +  return (enum btf_core_reloc_kind) read_kind (args[1], 1,
> +                                            BPF_RELO_TYPE_ID_LOCAL);
> +}
> +static inline enum btf_core_reloc_kind
> +kind_preserve_type_info (tree *args, int nargs)
> +{
> +  KIND_EXPECT_NARGS (2,
> +     "wrong number of arguments for type info core builtin");
> +  char val = read_kind (args[1], 2, 0);
> +  switch (val)
> +    {
> +    case 0:
> +      return BPF_RELO_TYPE_EXISTS;
> +    case 1:
> +      return BPF_RELO_TYPE_SIZE;
> +    case 2:
> +      return BPF_RELO_TYPE_MATCHES;
> +    default:
> +      break;
> +    }
> +  return BPF_RELO_INVALID;
> +}
> +
> +
> +/* Required to overcome having different return type builtins to avoid 
> warnings
> +   at front-end and be able to share the same builtin definition and 
> permitting
> +   the PURE attribute to work.  */
> +hash_map<tree, tree> core_builtin_type_defs;
> +
> +static tree
> +get_core_builtin_fndecl_for_type (tree ret_type)
> +{
> +  tree *def = core_builtin_type_defs.get (ret_type);
> +  if (def)
> +    return *def;
> +
> +  tree rettype = build_function_type_list (ret_type, integer_type_node, 
> NULL);
> +  tree new_fndecl = add_builtin_function_ext_scope ("__builtin_core_reloc",
> +                                                 rettype,
> +                                                 BPF_BUILTIN_CORE_RELOC,
> +                                                 BUILT_IN_MD, NULL, NULL);
> +  DECL_PURE_P (new_fndecl) = 1;
> +
> +  core_builtin_type_defs.put (ret_type, new_fndecl);
> +
> +  return new_fndecl;
> +}
> +
> +void
> +bpf_handle_plugin_finish_type (void *event_data,
> +                            void *data ATTRIBUTE_UNUSED)
> +{
> +  tree type = (tree) event_data;
> +
> +  if (TREE_CODE (type) == ENUMERAL_TYPE)
> +    for (tree l = TYPE_VALUES (type); l; l = TREE_CHAIN (l))
> +      {
> +     tree value = TREE_VALUE (l);
> +
> +     tree initial = DECL_INITIAL (value);
> +     initial = copy_node (initial);
> +     DECL_INITIAL (value) = initial;
> +
> +     bpf_enum_mappings.put (initial, value);
> +      }
> +}
> +
> +/* -- Header file exposed functions -- */
> +
> +/* Initializes support information to process CO-RE builtins.
> +   Defines information for the builtin processing, such as helper functions 
> to
> +   support the builtin convertion.  */
> +
> +void
> +bpf_init_core_builtins (void)
> +{
> +  memset (core_builtin_helpers, 0, sizeof (core_builtin_helpers));
> +
> +  core_builtin_helpers[BPF_BUILTIN_PRESERVE_ACCESS_INDEX] =
> +    BPF_CORE_HELPER_SET (kind_access_index,
> +                      NULL,
> +                      pack_field_expr_for_access_index,
> +                      process_field_expr,
> +                      true);
> +  core_builtin_helpers[BPF_BUILTIN_PRESERVE_FIELD_INFO] =
> +    BPF_CORE_HELPER_SET (kind_preserve_field_info,
> +                      NULL,
> +                      pack_field_expr_for_preserve_field,
> +                      process_field_expr,
> +                      true);
> +  core_builtin_helpers[BPF_BUILTIN_BTF_TYPE_ID] =
> +    BPF_CORE_HELPER_SET (kind_type_id,
> +                      compare_same_ptr_type,
> +                      pack_type,
> +                      process_type,
> +                      true);
> +
> +  core_builtin_helpers[BPF_BUILTIN_PRESERVE_TYPE_INFO] =
> +    BPF_CORE_HELPER_SET (kind_preserve_type_info,
> +                      compare_same_ptr_type,
> +                      pack_type,
> +                      process_type,
> +                      true);
> +
> +  core_builtin_helpers[BPF_BUILTIN_PRESERVE_ENUM_VALUE] =
> +    BPF_CORE_HELPER_SET (kind_enum_value,
> +                      compare_same_ptr_expr,
> +                      pack_enum_value,
> +                      process_enum_value,
> +                      true);
> +
> +  core_builtin_helpers[BPF_BUILTIN_CORE_RELOC] =
> +    BPF_CORE_HELPER_SET (NULL, NULL, NULL, NULL, true);
> +
> +  /* Initialize plugin handler to record enums value for use in
> +   * __builtin_preserve_enum_value.  */
> +  plugin_state = (enum bpf_plugin_states) flag_plugin_added;
> +  flag_plugin_added = true;
> +  register_callback ("bpf_collect_enum_info", PLUGIN_FINISH_TYPE,
> +                  bpf_handle_plugin_finish_type, NULL);
> +}
> +
> +static tree
> +construct_builtin_core_reloc (location_t loc, tree fndecl, tree *args,
> +                           int nargs)
> +{
> +  int code = DECL_MD_FUNCTION_CODE (fndecl);
> +  builtin_helpers helper = core_builtin_helpers[code];
> +
> +  if (helper.is_valid)
> +    {
> +      gcc_assert (helper.kind);
> +      gcc_assert (helper.pack);
> +      gcc_assert (helper.process);
> +
> +      struct cr_local local_data = CR_LOCAL_EMPTY;
> +      local_data.fail = false;
> +
> +      enum btf_core_reloc_kind kind = helper.kind (args, nargs);
> +      if (kind == BPF_RELO_INVALID)
> +     local_data.fail = true;
> +      else if (helper.pack != NULL)
> +     {
> +       local_data = helper.pack (args, kind, (enum bpf_builtins) code);
> +       local_data.reloc_data.orig_builtin_code = (enum bpf_builtins) code;
> +       local_data.reloc_data.orig_arg_expr = args[0];
> +     }
> +      else
> +     local_data.reloc_decision = KEEP_ORIGINAL_NO_RELOCATION;
> +
> +      if (local_data.fail == true)
> +     return error_mark_node;
> +
> +      if (local_data.reloc_decision == REPLACE_NO_RELOCATION)
> +     return local_data.reloc_data.expr;
> +      else if (local_data.reloc_decision == REPLACE_CREATE_RELOCATION)
> +     {
> +       int index = search_builtin_data (helper.compare,
> +                                        &local_data.reloc_data);
> +       if (index == -1)
> +         index = allocate_builtin_data ();
> +       struct cr_builtins *data = get_builtin_data (index);
> +       memcpy (data, &local_data.reloc_data, sizeof (struct cr_builtins));
> +
> +       tree new_fndecl = bpf_builtins[BPF_BUILTIN_CORE_RELOC];
> +
> +       tree ret_type = TREE_TYPE (local_data.reloc_data.default_value);
> +       if (ret_type != ptr_type_node)
> +         new_fndecl = get_core_builtin_fndecl_for_type (ret_type);
> +       return build_call_expr_loc (loc,
> +                                   new_fndecl, 1,
> +                                   build_int_cst (integer_type_node,
> +                                                  index));
> +     }
> +    }
> +  return NULL_TREE;
> +}
> +
> +/* This function is used by bpf_resolve_overloaded_builtin which is the
> +   implementation of the TARGET_RESOLVE_OVERLOADED_BUILTIN.  It is executed 
> in
> +   a very early stage and allows to adapt the builtin to different arguments
> +   allowing the compiler to make builtins polymorphic.  In this particular
> +   implementation, it collects information of the specific builtin call,
> +   converts it to the internal __builtin_core_reloc, stores any required
> +   information from the original builtin call in a vec<cr_builtins> and 
> assigns
> +   the index within the *vec*, replacing by __builtin_core_reloc.  In the
> +   process we also adjust return type of the __builtin_core_reloc to permit
> +   polymorphic return type, as it is expected in some of the BPF CO-RE
> +   builtins.  */
> +
> +#define MAX_CORE_BUILTIN_ARGS 3
> +tree
> +bpf_resolve_overloaded_core_builtin (location_t loc, tree fndecl,
> +                                  void *arglist)
> +{
> +  if (!bpf_require_core_support ())
> +    return error_mark_node;
> +
> +  vec<tree, va_gc> *argsvec = static_cast<vec<tree, va_gc> *> (arglist);
> +  tree args[MAX_CORE_BUILTIN_ARGS];
> +  for (unsigned int i = 0; i < argsvec->length (); i++)
> +    args[i] = (*argsvec)[i];
> +
> +  remove_parser_plugin ();
> +
> +  return construct_builtin_core_reloc (loc, fndecl, args, argsvec->length 
> ());
> +}
> +
> +/* Used in bpf_expand_builtin.  This function is called in RTL expand stage 
> to
> +   convert the internal __builtin_core_reloc in unspec:UNSPEC_CORE_RELOC RTL,
> +   which will contain a third argument that is the index in the vec 
> collected in
> +   bpf_resolve_overloaded_core_builtin.  */
> +
> +rtx
> +bpf_expand_core_builtin (tree exp, enum bpf_builtins code)
> +{
> +  if (code == BPF_BUILTIN_CORE_RELOC)
> +    {
> +      tree index = CALL_EXPR_ARG (exp, 0);
> +      struct cr_builtins *data = get_builtin_data (TREE_INT_CST_LOW (index));
> +
> +      rtx v = expand_normal (data->default_value);
> +      rtx i = expand_normal (index);
> +      return gen_rtx_UNSPEC (DImode,
> +                          gen_rtvec (2, v, i),
> +                          UNSPEC_CORE_RELOC);
> +    }
> +
> +  return NULL_RTX;
> +}
> +
> +/* This function is called in the final assembly output for the
> +   unspec:UNSPEC_CORE_RELOC.  It recovers the vec index kept as the third
> +   operand and collects the data from the vec.  With that it calls the 
> process
> +   helper in order to construct the data required for the CO-RE relocation.
> +   Also it creates a label pointing to the unspec instruction and uses it
> +   in the CO-RE relocation creation.  */
> +
> +const char *
> +bpf_add_core_reloc (rtx *operands, const char *templ)
> +{
> +  struct cr_builtins *data = get_builtin_data (INTVAL (operands[2]));
> +  builtin_helpers helper;
> +  helper = core_builtin_helpers[data->orig_builtin_code];
> +
> +  rtx_code_label * tmp_label = gen_label_rtx ();
> +  output_asm_label (tmp_label);
> +  assemble_name (asm_out_file, ":\n");
> +
> +  gcc_assert (helper.process != NULL);
> +  struct cr_final reloc_data = helper.process (data);
> +  make_core_relo (&reloc_data, tmp_label);
> +
> +  /* Replace default value for later processing builtin types.
> +     Example if the type id builtins. */
> +  if (data->rtx_default_value != NULL_RTX)
> +    operands[1] = data->rtx_default_value;
> +
> +  return templ;
> +}
> +
> +/* This function is used within the defined_expand for mov in bpf.md file.
> +   It identifies if any of the operands in a move is a expression with a
> +   type with __attribute__((preserve_access_index)), which case it
> +   will emit an unspec:UNSPEC_CORE_RELOC such that it would later create a
> +   CO-RE relocation for this expression access.  */
> +
> +void
> +bpf_replace_core_move_operands (rtx *operands)
> +{
> +  for (int i = 0; i < 2; i++)
> +    if (MEM_P (operands[i]))
> +      {
> +     tree expr = MEM_EXPR (operands[i]);
> +
> +     if (expr == NULL_TREE)
> +       continue;
> +
> +     if (TREE_CODE (expr) == MEM_REF
> +         && TREE_CODE (TREE_OPERAND (expr, 0)) == SSA_NAME)
> +       {
> +         gimple *def_stmt = SSA_NAME_DEF_STMT (TREE_OPERAND (expr, 0));
> +         if (def_stmt && is_gimple_assign (def_stmt))
> +             expr = gimple_assign_rhs1 (def_stmt);
> +       }
> +     if (is_attr_preserve_access (expr)
> +         && bpf_require_core_support ())
> +       {
> +         struct cr_local local_data = pack_field_expr_for_access_index (
> +                                        &expr,
> +                                        BPF_RELO_FIELD_BYTE_OFFSET,
> +                                        BPF_BUILTIN_PRESERVE_ACCESS_INDEX);
> +
> +         local_data.reloc_decision = REPLACE_CREATE_RELOCATION;
> +         local_data.reloc_data.orig_arg_expr = expr;
> +         local_data.reloc_data.orig_builtin_code = 
> BPF_BUILTIN_PRESERVE_ACCESS_INDEX;
> +
> +         int index = allocate_builtin_data ();
> +         struct cr_builtins *data = get_builtin_data (index);
> +         memcpy (data, &local_data.reloc_data, sizeof (struct cr_builtins));
> +
> +         rtx reg = XEXP (operands[i], 0);
> +         if (!REG_P (reg))
> +           {
> +             reg = gen_reg_rtx (Pmode);
> +             operands[i] = gen_rtx_MEM (GET_MODE (operands[i]), reg);
> +           }
> +
> +         emit_insn (
> +           gen_mov_reloc_coredi (reg,
> +                                 gen_rtx_CONST_INT (Pmode, 0),
> +                                 gen_rtx_CONST_INT (Pmode, index)));
> +         return;
> +       }
> +      }
> +}
> diff --git a/gcc/config/bpf/core-builtins.h b/gcc/config/bpf/core-builtins.h
> new file mode 100644
> index 000000000000..15cd3d3a94e5
> --- /dev/null
> +++ b/gcc/config/bpf/core-builtins.h
> @@ -0,0 +1,35 @@
> +#ifndef BPF_CORE_BUILTINS_H
> +#define BPF_CORE_BUILTINS_H
> +
> +#include "coreout.h"
> +
> +enum bpf_builtins
> +{
> +  BPF_BUILTIN_UNUSED = 0,
> +  /* Built-ins for non-generic loads and stores.  */
> +  BPF_BUILTIN_LOAD_BYTE,
> +  BPF_BUILTIN_LOAD_HALF,
> +  BPF_BUILTIN_LOAD_WORD,
> +
> +  /* Compile Once - Run Everywhere (CO-RE) support.  */
> +  BPF_CORE_BUILTINS_MARKER = 10,
> +  BPF_BUILTIN_PRESERVE_ACCESS_INDEX,
> +  BPF_BUILTIN_PRESERVE_FIELD_INFO,
> +  BPF_BUILTIN_BTF_TYPE_ID,
> +  BPF_BUILTIN_PRESERVE_TYPE_INFO,
> +  BPF_BUILTIN_PRESERVE_ENUM_VALUE,
> +
> +  /* CO-RE INTERNAL reloc.  */
> +  BPF_BUILTIN_CORE_RELOC,
> +
> +  BPF_BUILTIN_MAX,
> +};
> +
> +extern GTY (()) tree bpf_builtins[(int) BPF_BUILTIN_MAX];
> +
> +void bpf_init_core_builtins (void);
> +rtx bpf_expand_core_builtin (tree exp, enum bpf_builtins code);
> +tree bpf_resolve_overloaded_core_builtin (location_t loc, tree fndecl,
> +                                       void *arglist);
> +
> +#endif
> diff --git a/gcc/config/bpf/coreout.cc b/gcc/config/bpf/coreout.cc
> index bd609ad6278f..b84585fb104e 100644
> --- a/gcc/config/bpf/coreout.cc
> +++ b/gcc/config/bpf/coreout.cc
> @@ -30,6 +30,7 @@
>  #include "ctfc.h"
>  #include "btf.h"
>  #include "rtl.h"
> +#include "tree-pretty-print.h"
>  
>  #include "coreout.h"
>  
> @@ -146,38 +147,37 @@ static char 
> btf_ext_info_section_label[MAX_BTF_EXT_LABEL_BYTES];
>  
>  static GTY (()) vec<bpf_core_section_ref, va_gc> *bpf_core_sections;
>  
> +struct bpf_core_extra {
> +  const char *accessor_str;
> +  tree type;
> +};
> +static hash_map<bpf_core_reloc_ref, struct bpf_core_extra *> 
> bpf_comment_info;
>  
>  /* Create a new BPF CO-RE relocation record, and add it to the appropriate
>     CO-RE section.  */
> -
>  void
>  bpf_core_reloc_add (const tree type, const char * section_name,
> -                 vec<unsigned int> *accessors, rtx_code_label *label,
> +                 const char *accessor,
> +                 rtx_code_label *label,
>                   enum btf_core_reloc_kind kind)
>  {
> -  char buf[40];
> -  unsigned int i, n = 0;
> -
> -  /* A valid CO-RE access must have at least one accessor.  */
> -  if (accessors->length () < 1)
> -    return;
> -
> -  for (i = 0; i < accessors->length () - 1; i++)
> -    n += snprintf (buf + n, sizeof (buf) - n, "%u:", (*accessors)[i]);
> -  snprintf (buf + n, sizeof (buf) - n, "%u", (*accessors)[i]);
> -
>    bpf_core_reloc_ref bpfcr = ggc_cleared_alloc<bpf_core_reloc_t> ();
> +  struct bpf_core_extra *info = ggc_cleared_alloc<struct bpf_core_extra> ();
>    ctf_container_ref ctfc = ctf_get_tu_ctfc ();
>  
>    /* Buffer the access string in the auxiliary strtab.  */
> -  ctf_add_string (ctfc, buf, &(bpfcr->bpfcr_astr_off), CTF_AUX_STRTAB);
> -
> +  ctf_add_string (ctfc, accessor, &(bpfcr->bpfcr_astr_off), CTF_AUX_STRTAB);
>    bpfcr->bpfcr_type = get_btf_id (ctf_lookup_tree_type (ctfc, type));
>    bpfcr->bpfcr_insn_label = label;
>    bpfcr->bpfcr_kind = kind;
>  
> +  info->accessor_str = accessor;
> +  info->type = type;
> +  bpf_comment_info.put (bpfcr, info);
> +
>    /* Add the CO-RE reloc to the appropriate section.  */
>    bpf_core_section_ref sec;
> +  int i;
>    FOR_EACH_VEC_ELT (*bpf_core_sections, i, sec)
>      if (strcmp (sec->name, section_name) == 0)
>        {
> @@ -288,14 +288,26 @@ output_btfext_header (void)
>  static void
>  output_asm_btfext_core_reloc (bpf_core_reloc_ref bpfcr)
>  {
> +  struct bpf_core_extra **info = bpf_comment_info.get (bpfcr);
> +  gcc_assert (info != NULL);
> +
>    bpfcr->bpfcr_astr_off += ctfc_get_strtab_len (ctf_get_tu_ctfc (),
>                                               CTF_STRTAB);
>  
>    dw2_assemble_integer (4, gen_rtx_LABEL_REF (Pmode, 
> bpfcr->bpfcr_insn_label));
> -  fprintf (asm_out_file, "\t%s bpfcr_insn\n", ASM_COMMENT_START);
> -
> -  dw2_asm_output_data (4, bpfcr->bpfcr_type, "bpfcr_type");
> -  dw2_asm_output_data (4, bpfcr->bpfcr_astr_off, "bpfcr_astr_off");
> +  fprintf (asm_out_file, "\t%s%s\n",
> +        flag_debug_asm ? ASM_COMMENT_START : "",
> +        (flag_debug_asm ? " bpfcr_insn" : ""));
> +
> +  /* Extract the pretty print for the type expression.  */
> +  pretty_printer pp;
> +  dump_generic_node (&pp, (*info)->type, 0, TDF_VOPS|TDF_MEMSYMS|TDF_SLIM,
> +                  false);
> +  char *str = xstrdup (pp_formatted_text (&pp));
> +
> +  dw2_asm_output_data (4, bpfcr->bpfcr_type, "bpfcr_type (%s)", str);
> +  dw2_asm_output_data (4, bpfcr->bpfcr_astr_off, "bpfcr_astr_off (\"%s\")",
> +                       (*info)->accessor_str);
>    dw2_asm_output_data (4, bpfcr->bpfcr_kind, "bpfcr_kind");
>  }
>  
> diff --git a/gcc/config/bpf/coreout.h b/gcc/config/bpf/coreout.h
> index 8bdb364b7228..c99b1ca885b2 100644
> --- a/gcc/config/bpf/coreout.h
> +++ b/gcc/config/bpf/coreout.h
> @@ -23,6 +23,7 @@
>  #define __COREOUT_H
>  
>  #include <stdint.h>
> +#include "ctfc.h"
>  
>  #ifdef       __cplusplus
>  extern "C"
> @@ -55,6 +56,7 @@ struct btf_ext_lineinfo
>  
>  enum btf_core_reloc_kind
>  {
> +  BPF_RELO_INVALID = -1,
>    BPF_RELO_FIELD_BYTE_OFFSET = 0,
>    BPF_RELO_FIELD_BYTE_SIZE = 1,
>    BPF_RELO_FIELD_EXISTS = 2,
> @@ -66,7 +68,8 @@ enum btf_core_reloc_kind
>    BPF_RELO_TYPE_EXISTS = 8,
>    BPF_RELO_TYPE_SIZE = 9,
>    BPF_RELO_ENUMVAL_EXISTS = 10,
> -  BPF_RELO_ENUMVAL_VALUE = 11
> +  BPF_RELO_ENUMVAL_VALUE = 11,
> +  BPF_RELO_TYPE_MATCHES = 12
>  };
>  
>  struct btf_ext_reloc
> @@ -102,8 +105,12 @@ struct btf_ext_header
>  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 *, enum btf_core_reloc_kind);
> +void
> +bpf_core_reloc_add (const tree type, const char * section_name,
> +                 const char *accessor,
> +                 rtx_code_label *label,
> +                 enum btf_core_reloc_kind kind);
> +
>  extern int bpf_core_get_sou_member_index (ctf_container_ref, const tree);
>  
>  #ifdef       __cplusplus
> diff --git a/gcc/config/bpf/t-bpf b/gcc/config/bpf/t-bpf
> index 3f3cf8daf8fc..c289dde8b173 100644
> --- a/gcc/config/bpf/t-bpf
> +++ b/gcc/config/bpf/t-bpf
> @@ -1,8 +1,10 @@
>  
> -TM_H += $(srcdir)/config/bpf/coreout.h
> +TM_H += $(srcdir)/config/bpf/coreout.h $(srcdir)/config/bpf/core-builtins.h
>  
>  coreout.o: $(srcdir)/config/bpf/coreout.cc
>       $(COMPILE) $<
>       $(POSTCOMPILE)
>  
> -PASSES_EXTRA += $(srcdir)/config/bpf/bpf-passes.def
> +core-builtins.o: $(srcdir)/config/bpf/core-builtins.cc
> +     $(COMPILE) $<
> +     $(POSTCOMPILE)
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 97eaacf8a7ec..e06caf38e467 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -16015,6 +16015,57 @@ read_y (struct S *arg)
>  @end smallexample
>  @enddefbuiltin
>  
> +@defbuiltin{{unsigned int} __builtin_preserve_enum_value (@var{type}, 
> @var{enum}, unsigned int @var{kind})}
> +BPF Compile Once-Run Everywhere (CO-RE) support. This builtin collects enum
> +information and creates a CO-RE relocation relative to @var{enum} that should
> +be of @var{type}.  The @var{kind} specifies the action performed.
> +
> +The following values are supported for @var{kind}:
> +@table @code
> +@item ENUM_VALUE_EXISTS = 0
> +The return value is either 0 or 1 depending if the enum value exists in the
> +target.
> +
> +@item ENUM_VALUE = 1
> +The return value is the enum value in the target kernel.
> +@end table
> +@enddefbuiltin
> +
> +@defbuiltin{{unsigned int} __builtin_btf_type_id (@var{type}, unsigned int 
> @var{kind})}
> +BPF Compile Once-Run Everywhere (CO-RE) support. This builtin is used to get
> +the BTF type ID of a specified type. Depending on the @var{kind} argument, it
> +will either return the ID of the local BTF information, or the BTF type ID in
> +the target kernel.
> +
> +The following values are supported for @var{kind}:
> +@table @code
> +@item BTF_TYPE_ID_LOCAL = 0
> +Return the local BTF type ID. Always succeeds.
> +
> +@item BTF_TYPE_ID_TARGET = 1
> +Return the target BTF type ID. If type does not exist in the target, returns 
> 0.
> +@end table
> +@enddefbuiltin
> +
> +@defbuiltin{{unsigned int} __builtin_preserve_type_info (@var{type}, 
> unsigned int @var{kind})}
> +BPF Compile Once-Run Everywhere (CO-RE) support. This builtin performs named
> +type (struct/union/enum/typedef) verifications. The type of verification
> +dependents on the @var{kind} argument provided.  This builtin will always
> +return 0 if type does not exists in the target kernel.
> +
> +The following values are supported for @var{kind}:
> +@table @code
> +@item BTF_TYPE_EXISTS = 0
> +Checks if type exists in the target.
> +
> +@item BTF_TYPE_MATCHES = 1
> +Checks if type matches the local definition in the target kernel.
> +
> +@item BTF_TYPE_SIZE = 2
> +Returns the size of the type within the target.
> +@end table
> +@enddefbuiltin
> +
>  @node FR-V Built-in Functions
>  @subsection FR-V Built-in Functions

Reply via email to