Pushed to upstream master.

Thanks !

Jose E. Marchesi writes:

> 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