[v4] bpf: Improvements in CO-RE builtins implementation.

Message ID 875y2ta0bf.fsf@oracle.com
State Unresolved
Headers
Series [v4] bpf: Improvements in CO-RE builtins implementation. |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Cupertino Miranda Oct. 26, 2023, 3:08 p.m. UTC
  Changes from v1:
 - Fixed Davids remarks on initial patch.
 - Fixed mistake with deleted '*'.

Changes from v2:
 - Reversed return value for bpf_const_not_ok_for_debug_p function.

Changes from v3:
 - Fixed ICE in two bpf-next tests:
     -      if (!wi->is_lhs)
     -       core_mark_as_access_index (gimple_get_lhs (wi->stmt));
     +      tree lhs;
     +      if (!wi->is_lhs
     +         && (lhs = gimple_get_lhs (wi->stmt)) != NULL_TREE)
     +       core_mark_as_access_index (lhs);
  

Comments

David Faust Oct. 26, 2023, 4:44 p.m. UTC | #1
On 10/26/23 08:08, Cupertino Miranda wrote:
> 
> Changes from v1:
>  - Fixed Davids remarks on initial patch.
>  - Fixed mistake with deleted '*'.
> 
> Changes from v2:
>  - Reversed return value for bpf_const_not_ok_for_debug_p function.

Hmm..

> +static bool
> +bpf_const_not_ok_for_debug_p (rtx p)
> +{
> +  if (GET_CODE (p) == UNSPEC
> +      && XINT (p, 1) == UNSPEC_CORE_RELOC)
> +    return false;
> +
> +  return true;
> +}
> +
> +#undef TARGET_CONST_NOT_OK_FOR_DEBUG_P
> +#define TARGET_CONST_NOT_OK_FOR_DEBUG_P bpf_const_not_ok_for_debug_p

 -- Target Hook: bool TARGET_CONST_NOT_OK_FOR_DEBUG_P (rtx X)
     This hook should return true if X should not be emitted into debug
     sections.

As written now, won't this cause all ordinary (non-UNSPEC_CORE_RELOC)
consts to get rejected for debug? ("regular" debug i.e. DWARF, not to
be confused with the BTF.ext holding CO-RE relocs).

I see other targets implementing the hook returning true only in
specific cases and false otherwise.  The implementation in v1 makes
more sense to me.  Could you explain why flip the return value?

> 
> Changes from v3:
>  - Fixed ICE in two bpf-next tests:
>      -      if (!wi->is_lhs)
>      -       core_mark_as_access_index (gimple_get_lhs (wi->stmt));
>      +      tree lhs;
>      +      if (!wi->is_lhs
>      +         && (lhs = gimple_get_lhs (wi->stmt)) != NULL_TREE)
>      +       core_mark_as_access_index (lhs);
>
  
Cupertino Miranda Oct. 27, 2023, 7:43 p.m. UTC | #2
Hi David,

David Faust writes:

> On 10/26/23 08:08, Cupertino Miranda wrote:
>>
>> Changes from v1:
>>  - Fixed Davids remarks on initial patch.
>>  - Fixed mistake with deleted '*'.
>>
>> Changes from v2:
>>  - Reversed return value for bpf_const_not_ok_for_debug_p function.
>
> Hmm..
>
>> +static bool
>> +bpf_const_not_ok_for_debug_p (rtx p)
>> +{
>> +  if (GET_CODE (p) == UNSPEC
>> +      && XINT (p, 1) == UNSPEC_CORE_RELOC)
>> +    return false;
>> +
>> +  return true;
>> +}
>> +
>> +#undef TARGET_CONST_NOT_OK_FOR_DEBUG_P
>> +#define TARGET_CONST_NOT_OK_FOR_DEBUG_P bpf_const_not_ok_for_debug_p
>
>  -- Target Hook: bool TARGET_CONST_NOT_OK_FOR_DEBUG_P (rtx X)
>      This hook should return true if X should not be emitted into debug
>      sections.
>
> As written now, won't this cause all ordinary (non-UNSPEC_CORE_RELOC)
> consts to get rejected for debug? ("regular" debug i.e. DWARF, not to
> be confused with the BTF.ext holding CO-RE relocs).
>
> I see other targets implementing the hook returning true only in
> specific cases and false otherwise.  The implementation in v1 makes
> more sense to me.  Could you explain why flip the return value?
It turns out that defining this hook is not the proper solution.
I am trying a different approach which I believe is better.

Thanks,
Cupertino

>>
>> Changes from v3:
>>  - Fixed ICE in two bpf-next tests:
>>      -      if (!wi->is_lhs)
>>      -       core_mark_as_access_index (gimple_get_lhs (wi->stmt));
>>      +      tree lhs;
>>      +      if (!wi->is_lhs
>>      +         && (lhs = gimple_get_lhs (wi->stmt)) != NULL_TREE)
>>      +       core_mark_as_access_index (lhs);
>>
  

Patch

commit b525feaeb159f55c2a6db1cb4246bd027351f2c5
Author: Cupertino Miranda <cupertino.miranda@oracle.com>
Date:   Tue Aug 8 09:22:41 2023 +0100

    bpf: Improvements in CO-RE builtins implementation.
    
    This patch moved the processing of attribute preserve_access_index to
    its own independent pass in a gimple lowering pass.
    This approach is more consistent with the implementation of the CO-RE
    builtins when used explicitly in the code.  The attributed type accesses
    are now early converted to __builtin_core_reloc builtin instead of being
    kept as an expression in code through out all of the middle-end.
    This disables the compiler to optimize out or manipulate the expression
    using the local defined type, instead of assuming nothing is known about
    this expression, as it should be the case in all of the CO-RE
    relocations.
    
    In the process, also the __builtin_preserve_access_index has been
    improved to generate code for more complex expressions that would
    require more then one CO-RE relocation.
    This turned out to be a requirement, since bpf-next selftests would rely on
    loop unrolling in order to convert an undefined index array access into a
    defined one. This seemed extreme to expect for the unroll to happen, and for
    that reason GCC still generates correct code in such scenarios, even when index
    access is never predictable or unrolling does not occur.
    
    gcc/ChangeLog:
            * config/bpf/bpf-passes.def (pass_lower_bpf_core): Added pass.
            * config/bpf/bpf-protos.h: Added prototype for new pass.
            * config/bpf/bpf.cc (bpf_const_not_ok_for_debug_p): New function.
            * config/bpf/bpf.md (mov_reloc_core<MM:mode>): Prefixed
            name with '*'.
            * config/bpf/core-builtins.cc (cr_builtins) Added access_node to
            struct.
            (is_attr_preserve_access): Improved check.
            (core_field_info): Make use of root_for_core_field_info
            function.
            (process_field_expr): Adapted to new functions.
            (pack_type): Small improvement.
            (bpf_handle_plugin_finish_type): Adapted to GTY(()).
            (bpf_init_core_builtins): Changed to new function names.
            (construct_builtin_core_reloc): Improved implementation.
            (bpf_resolve_overloaded_core_builtin): Changed how
            __builtin_preserve_access_index is converted.
            (compute_field_expr): Corrected implementation. Added
            access_node argument.
            (bpf_core_get_index): Added valid argument.
            (root_for_core_field_info, pack_field_expr)
            (core_expr_with_field_expr_plus_base, make_core_safe_access_index)
            (replace_core_access_index_comp_expr, maybe_get_base_for_field_expr)
            (core_access_clean, core_is_access_index, core_mark_as_access_index)
            (make_gimple_core_safe_access_index, execute_lower_bpf_core)
            (make_pass_lower_bpf_core): Added functions.
            (pass_data_lower_bpf_core): New pass struct.
            (pass_lower_bpf_core): New gimple_opt_pass class.
            (pack_field_expr_for_preserve_field)
            (bpf_replace_core_move_operands): Removed function.
            (bpf_enum_value_kind): Added GTY(()).
            * config/bpf/core-builtins.h (bpf_field_info_kind, bpf_type_id_kind)
            (bpf_type_info_kind, bpf_enum_value_kind): New enum.
            * config/bpf/t-bpf: Added pass bpf-passes.def to PASSES_EXTRA.
    
    gcc/testsuite/ChangeLog:
            * gcc.target/bpf/core-attr-5.c: New test.
            * gcc.target/bpf/core-attr-6.c: New test.
            * gcc.target/bpf/core-builtin-1.c: Corrected
            * gcc.target/bpf/core-builtin-enumvalue-opt.c: Corrected regular
            expression.
            * gcc.target/bpf/core-builtin-enumvalue.c: Corrected regular
            expression.
            * gcc.target/bpf/core-builtin-exprlist-1.c: New test.
            * gcc.target/bpf/core-builtin-exprlist-2.c: New test.
            * gcc.target/bpf/core-builtin-exprlist-3.c: New test.
            * gcc.target/bpf/core-builtin-exprlist-4.c: New test.
            * gcc.target/bpf/core-builtin-fieldinfo-offset-1.c: Extra tests

diff --git a/gcc/config/bpf/bpf-passes.def b/gcc/config/bpf/bpf-passes.def
new file mode 100644
index 000000000000..0ec20eac965d
--- /dev/null
+++ b/gcc/config/bpf/bpf-passes.def
@@ -0,0 +1,20 @@ 
+/* Declaration of target-specific passes for eBPF.
+   Copyright (C) 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_BEFORE (pass_lower_omp, 1, pass_lower_bpf_core);
diff --git a/gcc/config/bpf/bpf-protos.h b/gcc/config/bpf/bpf-protos.h
index fbe0d8a0213f..ecbbd7870e13 100644
--- a/gcc/config/bpf/bpf-protos.h
+++ b/gcc/config/bpf/bpf-protos.h
@@ -31,6 +31,8 @@  extern void bpf_expand_prologue (void);
 extern void bpf_expand_epilogue (void);
 extern void bpf_expand_cbranch (machine_mode, rtx *);
 const char *bpf_add_core_reloc (rtx *operands, const char *templ);
-void bpf_replace_core_move_operands (rtx *operands);
+
+class gimple_opt_pass;
+gimple_opt_pass *make_pass_lower_bpf_core (gcc::context *ctxt);
 
 #endif /* ! GCC_BPF_PROTOS_H */
diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
index 437bd652de3e..7a88187e02a9 100644
--- a/gcc/config/bpf/bpf.cc
+++ b/gcc/config/bpf/bpf.cc
@@ -1034,6 +1034,18 @@  bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
 #undef TARGET_RESOLVE_OVERLOADED_BUILTIN
 #define TARGET_RESOLVE_OVERLOADED_BUILTIN bpf_resolve_overloaded_builtin
 
+static bool
+bpf_const_not_ok_for_debug_p (rtx p)
+{
+  if (GET_CODE (p) == UNSPEC
+      && XINT (p, 1) == UNSPEC_CORE_RELOC)
+    return false;
+
+  return true;
+}
+
+#undef TARGET_CONST_NOT_OK_FOR_DEBUG_P
+#define TARGET_CONST_NOT_OK_FOR_DEBUG_P bpf_const_not_ok_for_debug_p
 
 /* Initialize target-specific function library calls.  This is mainly
    used to call library-provided soft-fp operations, since eBPF
diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md
index e87d72182bb8..0e2ad8da5ace 100644
--- a/gcc/config/bpf/bpf.md
+++ b/gcc/config/bpf/bpf.md
@@ -374,8 +374,6 @@ 
         ""
         "
 {
-  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]);
@@ -393,7 +391,7 @@ 
    {st<mop>\t%0,%1|*(<smop> *) (%0) = %1}"
 [(set_attr "type" "ldx,alu,alu,stx,st")])
 
-(define_insn "mov_reloc_core<MM:mode>"
+(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")
diff --git a/gcc/config/bpf/core-builtins.cc b/gcc/config/bpf/core-builtins.cc
index 60d21a8ae739..a224847d5d91 100644
--- a/gcc/config/bpf/core-builtins.cc
+++ b/gcc/config/bpf/core-builtins.cc
@@ -38,97 +38,96 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimple.h"
 #include "gimple-iterator.h"
 #include "gimple-walk.h"
+#include "gimple-fold.h"
 #include "tree-pass.h"
 #include "plugin.h"
+#include "gimplify.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.  */
-
+/* 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_init_core_builtins 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_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), also referred through the document as
+      an field expression,  within the argument will be recorded by the CO-RE
+      machinery, resulting in one or more relocations being inserted in the
+      .BTF.ext section of the output.
+
+    - An attribute, __attribute__((preserve_access_index))
+
+      This attribute can be applied to struct and union types.  Any access done
+      through a node typed with this attribute will be recorded by the CO-RE
+      machinery.  This conversion is done in an independent gimple pass very
+      early in compilation, making sure that the field expression is
+      originating from a tree node which his type is attributed.
+
+    Both these variants of preserve_access_index rely on a tree walker that
+    identifies and converts any CO-RE valid field expressions.  Apart from the
+    gimple specific requirements for the attribute implementation both builtin
+    and attribute implementations rely on the same mechanism.  */
 
 struct GTY(()) cr_builtins
 {
@@ -139,12 +138,13 @@  struct GTY(()) cr_builtins
   enum btf_core_reloc_kind kind;
   enum bpf_builtins orig_builtin_code;
   tree orig_arg_expr;
+  tree access_node;
 };
 typedef struct cr_builtins *cr_builtins_ref;
 
 #define CORE_BUILTINS_DATA_EMPTY \
   { NULL_TREE, NULL_TREE, NULL_TREE, NULL_RTX, BPF_RELO_INVALID, \
-    BPF_BUILTIN_UNUSED, NULL }
+    BPF_BUILTIN_UNUSED, NULL_TREE, NULL_TREE}
 
 /* Vector definition and its access function.  */
 static GTY(()) vec<cr_builtins_ref, va_gc> *builtins_data = NULL;
@@ -189,7 +189,6 @@  search_builtin_data (builtin_local_data_compare_fn callback,
 enum cr_decision
 {
   FAILED_VALIDATION = 0,
-  KEEP_ORIGINAL_NO_RELOCATION,
   REPLACE_CREATE_RELOCATION,
   REPLACE_NO_RELOCATION
 };
@@ -286,7 +285,7 @@  compare_same_ptr_type (struct cr_builtins *a, struct cr_builtins *b)
 
 /* Handling for __attribute__((preserve_access_index)) for BPF CO-RE support.
 
-   This attribute marks a structure/union/array type as "preseve", so that
+   This attribute marks a structure/union/array type as "preserve", 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.
@@ -300,7 +299,7 @@  compare_same_ptr_type (struct cr_builtins *a, struct cr_builtins *b)
    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 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.  */
@@ -319,11 +318,14 @@  is_attr_preserve_access (tree t)
   tree base = get_inner_reference (t, &bitsize, &bitpos, &var_off, &mode,
 				   &sign, &reverse, &vol);
 
+  if (TREE_CODE (t) == SSA_NAME
+      || TREE_CODE (t) == VAR_DECL)
+    return lookup_attribute ("preserve_access_index",
+			     TYPE_ATTRIBUTES (TREE_TYPE (base)));
+
   if (TREE_CODE (base) == MEM_REF)
-    {
-      return lookup_attribute ("preserve_access_index",
-			       TYPE_ATTRIBUTES (TREE_TYPE (base)));
-    }
+    return lookup_attribute ("preserve_access_index",
+			     TYPE_ATTRIBUTES (TREE_TYPE (base)));
 
   if (TREE_CODE (t) == COMPONENT_REF)
     {
@@ -339,7 +341,8 @@  is_attr_preserve_access (tree t)
       const tree container = DECL_CONTEXT (TREE_OPERAND (t, 1));
 
       return lookup_attribute ("preserve_access_index",
-			       TYPE_ATTRIBUTES (container));
+			       TYPE_ATTRIBUTES (container))
+	     || is_attr_preserve_access (op);
     }
 
   else if (TREE_CODE (t) == ADDR_EXPR)
@@ -348,6 +351,25 @@  is_attr_preserve_access (tree t)
   return false;
 }
 
+static tree
+root_for_core_field_info (tree node)
+{
+  bool done = false;
+  while (!done)
+    {
+      switch (TREE_CODE (node))
+	{
+	case ADDR_EXPR:
+	case NOP_EXPR:
+	  node = TREE_OPERAND (node, 0);
+	  break;
+	default:
+	  done = true;
+	  break;
+	}
+    }
+  return node;
+}
 
 /* Expand a call to __builtin_preserve_field_info by evaluating the requested
    information about SRC according to KIND, and return a tree holding
@@ -364,6 +386,8 @@  core_field_info (tree src, enum btf_core_reloc_kind kind)
   location_t loc = EXPR_LOCATION (src);
   tree type = TREE_TYPE (src);
 
+  src = root_for_core_field_info (src);
+
   get_inner_reference (src, &bitsize, &bitpos, &var_off, &mode, &unsignedp,
 		       &reversep, &volatilep);
 
@@ -499,7 +523,7 @@  core_field_info (tree src, enum btf_core_reloc_kind kind)
    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)
+bpf_core_get_index (const tree node, bool *valid)
 {
   enum tree_code code = TREE_CODE (node);
 
@@ -539,66 +563,104 @@  bpf_core_get_index (const tree node)
 	}
     }
 
-  gcc_unreachable ();
+  if (valid != NULL)
+    *valid = false;
   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.  */
+#define PREPARE_FAKE_PTR(P) \
+  _fake_##P; \
+  if (P == NULL) \
+    P = &_fake_##P; \
+  _fake_##P
+
+#define MAX_NR_ACCESSORS 100
+
+/* This function validates and extracts information for CO-RE field expression.
+   Any parametric expression is allowed to be passed in argument NODE.
+
+   If NODE is a valid CO-RE expression VALID boolean pointer would be set to
+   true.
+
+   A CO-RE field expression is an expression accessing structures, arrays and
+   unions.
+   An examples of CO-RE valid expression is:
+     A->B[2].UNION_C.D
+
+   This function traverses the tree structure to verify if the expression in
+   NODE is valid and extracts other characteristics of the expression, and
+   returns it by updating the pointer arguments:
+
+   ACCESSORS: is an array with the indexes of the particular fields
+     within the expression.  The indexes are related to actual index on the
+     struct/union type, or the array access index.  The RETURN of the function
+     is the number of accessors required to represent this expression in CO-RE
+     access string.
+   VALID - boolean pointer that sets if expression is valid or not for CO-RE.
+   ACCESS_NODE - It is the base of the expression.  Using the example below is
+     the node that represents the A in the expression.
+
+   ALLOW_ENTRY_CAST is an input arguments and specifies if the function should
+   consider as valid expressions in which NODE entry is a cast expression (or
+   tree code nop_expr).  */
 
 static unsigned char
-compute_field_expr (tree node, unsigned int *accessors, bool *valid,
-		    tree *root)
+compute_field_expr (tree node, unsigned int *accessors,
+		    bool *valid,
+		    tree *access_node,
+		    bool allow_entry_cast = true)
 {
   unsigned char n = 0;
+  unsigned int fake_accessors[MAX_NR_ACCESSORS];
+  if (accessors == NULL)
+    accessors = fake_accessors;
+  bool PREPARE_FAKE_PTR (valid) = true;
+  tree PREPARE_FAKE_PTR (access_node) = NULL_TREE;
+
   if (node == NULL_TREE)
     {
       *valid = false;
       return 0;
     }
 
+  *access_node = node;
+
   switch (TREE_CODE (node))
     {
-    case INDIRECT_REF:
     case ADDR_EXPR:
+      return 0;
+    case INDIRECT_REF:
       accessors[0] = 0;
-      n = compute_field_expr (TREE_OPERAND (node, 0), &accessors[0], valid,
-			      root);
-      *root = node;
-      return n + 1;
+      return 1;
     case POINTER_PLUS_EXPR:
-      accessors[0] = bpf_core_get_index (node);
-      *root = node;
+      accessors[0] = bpf_core_get_index (node, valid);
       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;
+      n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
+			      valid,
+			      access_node, false);
+      accessors[n] = bpf_core_get_index (TREE_OPERAND (node, 1), valid);
       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;
+      n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
+			      valid,
+			      access_node, false);
+      accessors[n++] = bpf_core_get_index (node, valid);
+      return n;
     case NOP_EXPR:
-      n = compute_field_expr (TREE_OPERAND (node, 0), accessors, valid, root);
-      *root = node;
+      if (allow_entry_cast == true)
+	{
+	  *valid = false;
+	  return 0;
+	}
+      n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
+			      valid,
+			      access_node, false);
       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 CALL_EXPR:
     case SSA_NAME:
     case VAR_DECL:
     case PARM_DECL:
@@ -608,81 +670,46 @@  compute_field_expr (tree node, unsigned int *accessors, bool *valid,
       return 0;
     }
 }
+#undef PREPARE_FAKE_PTR
 
-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;
-}
+/* Pack helper for the __builtin_preserve_field_info.  */
 
 static struct cr_local
-pack_field_expr_for_preserve_field (tree *args,
-				    enum btf_core_reloc_kind kind,
-				    enum bpf_builtins code ATTRIBUTE_UNUSED)
+pack_field_expr (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;
+  tree access_node = NULL_TREE;
+  tree type = NULL_TREE;
 
-  /* 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;
+  ret.reloc_decision = REPLACE_CREATE_RELOCATION;
 
   unsigned int accessors[100];
   bool valid = true;
-  compute_field_expr (arg, accessors, &valid, &root);
+  compute_field_expr (root, accessors, &valid, &access_node, false);
+
+  type = TREE_TYPE (access_node);
 
   if (valid == true)
-    ret.reloc_data.expr = root;
+    {
+      ret.reloc_data.expr = root;
+
+      /* 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.access_node = access_node;
+      ret.reloc_data.type = type;
+      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;
+    }
   else
     {
       bpf_error_at (EXPR_LOC_OR_LOC (arg, UNKNOWN_LOCATION),
@@ -690,17 +717,11 @@  pack_field_expr_for_preserve_field (tree *args,
       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;
 }
 
+/* Process helper for the __builtin_preserve_field_info.  */
+
 static struct cr_final
 process_field_expr (struct cr_builtins *data)
 {
@@ -711,19 +732,18 @@  process_field_expr (struct cr_builtins *data)
 	      || data->kind == BPF_RELO_FIELD_SIGNED
 	      || data->kind == BPF_RELO_FIELD_EXISTS);
 
-  unsigned int accessors[100];
+  unsigned int accessors[MAX_NR_ACCESSORS];
   unsigned char nr_accessors = 0;
-  bool valid = true;
-  tree root = NULL_TREE;
   tree expr = data->expr;
-  tree type = TREE_TYPE (data->expr);
+  tree type = data->type;
 
   if (TREE_CODE (expr) == ADDR_EXPR)
     expr = TREE_OPERAND (expr, 0);
 
-  nr_accessors = compute_field_expr (expr, accessors, &valid, &root);
+  expr = root_for_core_field_info (expr);
+  nr_accessors = compute_field_expr (expr, accessors, NULL, NULL, false);
 
-  struct cr_final ret = { NULL, type, data->kind};
+  struct cr_final ret = { NULL, type, data->kind };
 
   char str[100];
   if (nr_accessors > 0)
@@ -740,9 +760,11 @@  process_field_expr (struct cr_builtins *data)
   return ret;
 }
 
-hash_map <tree, tree> bpf_enum_mappings;
-
+static GTY(()) hash_map<tree, tree> *bpf_enum_mappings;
 tree enum_value_type = NULL_TREE;
+
+/* Pack helper for the __builtin_preserve_enum_value.  */
+
 static struct cr_local
 pack_enum_value (tree *args, enum btf_core_reloc_kind kind,
 		 enum bpf_builtins code ATTRIBUTE_UNUSED)
@@ -757,7 +779,7 @@  pack_enum_value (tree *args, enum btf_core_reloc_kind kind,
   tree type = NULL_TREE;
 
   /* Deconstructing "*(typeof (enum_type) *) enum_value" to collect both the
-   * enum_type and enum_value.  */
+     enum_type and enum_value.  */
   if (TREE_CODE (tmp) != TARGET_EXPR
       || (type = TREE_TYPE (tmp)) == NULL_TREE
       || (TREE_CODE (type) != POINTER_TYPE)
@@ -771,7 +793,7 @@  pack_enum_value (tree *args, enum btf_core_reloc_kind kind,
   if (TREE_CODE (enum_value) != INTEGER_CST)
     goto pack_enum_value_fail;
 
-  result = bpf_enum_mappings.get (enum_value);
+  result = bpf_enum_mappings->get (enum_value);
   if (result == NULL)
     goto pack_enum_value_fail;
 
@@ -797,6 +819,8 @@  pack_enum_value_fail:
   return ret;
 }
 
+/* Process helper for the __builtin_preserve_enum_value.  */
+
 static struct cr_final
 process_enum_value (struct cr_builtins *data)
 {
@@ -829,6 +853,8 @@  process_enum_value (struct cr_builtins *data)
   return ret;
 }
 
+/* Pack helper for the __builtin_preserve_type_info.  */
+
 static struct cr_local
 pack_type (tree *args, enum btf_core_reloc_kind kind,
 	   enum bpf_builtins code ATTRIBUTE_UNUSED)
@@ -842,12 +868,12 @@  pack_type (tree *args, enum btf_core_reloc_kind kind,
   tree tmp = args[0];
   HOST_WIDE_INT type_size_i;
 
+  if (TYPE_P (tmp))
+    goto is_already_type;
   /* Typical structure to match:
-   *    *({ extern typeof (TYPE) *<tmp_name>; <tmp_name>; })
-   */
+	*({ 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))
@@ -865,6 +891,7 @@  pack_type (tree *args, enum btf_core_reloc_kind kind,
 
   tmp = TREE_TYPE (tmp);
 
+is_already_type:
   if (TREE_CODE (tmp) == POINTER_TYPE)
     tmp = TREE_TYPE (tmp);
 
@@ -884,7 +911,7 @@  pack_type (tree *args, enum btf_core_reloc_kind kind,
   ret.reloc_data.type = root_type;
   ret.reloc_decision = REPLACE_CREATE_RELOCATION;
 
-  /* Force this type to be marked as used in dwarf2out. */
+  /* 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);
@@ -915,11 +942,13 @@  pack_type (tree *args, enum btf_core_reloc_kind kind,
 
 pack_type_fail:
       bpf_error_at (EXPR_LOC_OR_LOC (args[0], UNKNOWN_LOCATION),
-		    "invelid first argument format for enum value builtin");
+		    "invalid first argument format for enum value builtin");
       ret.fail = true;
   return ret;
 }
 
+/* Process helper for the __builtin_preserve_type_info.  */
+
 static struct cr_final
 process_type (struct cr_builtins *data)
 {
@@ -1075,30 +1104,8 @@  kind_preserve_type_info (tree *args, int nargs)
   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;
-}
+/* Plugin handler used in the parser that allows to collect enum value
+   information that other wise would be folded and non recoverable.  */
 
 void
 bpf_handle_plugin_finish_type (void *event_data,
@@ -1106,6 +1113,9 @@  bpf_handle_plugin_finish_type (void *event_data,
 {
   tree type = (tree) event_data;
 
+  if (bpf_enum_mappings == NULL)
+    bpf_enum_mappings = hash_map<tree, tree>::create_ggc (10);
+
   if (TREE_CODE (type) == ENUMERAL_TYPE)
     for (tree l = TYPE_VALUES (type); l; l = TREE_CHAIN (l))
       {
@@ -1115,12 +1125,12 @@  bpf_handle_plugin_finish_type (void *event_data,
 	initial = copy_node (initial);
 	DECL_INITIAL (value) = initial;
 
-	bpf_enum_mappings.put (initial, value);
+	tree *found = bpf_enum_mappings->get (initial);
+	if (found == NULL)
+	  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.  */
@@ -1133,13 +1143,13 @@  bpf_init_core_builtins (void)
   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,
+			 NULL,
+			 NULL,
 			 true);
   core_builtin_helpers[BPF_BUILTIN_PRESERVE_FIELD_INFO] =
     BPF_CORE_HELPER_SET (kind_preserve_field_info,
 			 NULL,
-			 pack_field_expr_for_preserve_field,
+			 pack_field_expr,
 			 process_field_expr,
 			 true);
   core_builtin_helpers[BPF_BUILTIN_BTF_TYPE_ID] =
@@ -1167,13 +1177,17 @@  bpf_init_core_builtins (void)
     BPF_CORE_HELPER_SET (NULL, NULL, NULL, NULL, true);
 
   /* Initialize plugin handler to record enums value for use in
-   * __builtin_preserve_enum_value.  */
+     __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);
 }
 
+/* This function returns the related __builtin_core_reloc call tree node to a
+   particular CO-RE builtin definition in FNDECL when called with
+   arguments ARGS.  */
+
 static tree
 construct_builtin_core_reloc (location_t loc, tree fndecl, tree *args,
 			      int nargs)
@@ -1200,14 +1214,12 @@  construct_builtin_core_reloc (location_t loc, tree fndecl, tree *args,
 	  local_data.reloc_data.orig_arg_expr = args[0];
 	}
       else
-	local_data.reloc_decision = KEEP_ORIGINAL_NO_RELOCATION;
+	gcc_unreachable ();
 
       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)
+      if (local_data.reloc_decision == REPLACE_CREATE_RELOCATION)
 	{
 	  int index = search_builtin_data (helper.compare,
 					   &local_data.reloc_data);
@@ -1216,37 +1228,253 @@  construct_builtin_core_reloc (location_t loc, tree fndecl, tree *args,
 	  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 fndecl = bpf_builtins[BPF_BUILTIN_CORE_RELOC];
+	  return  build_call_expr_loc (loc,
+				fndecl, 1,
+				build_int_cst (integer_type_node, index));
+	}
+    }
+  return NULL_TREE;
+}
+
+/* This function constructs the CO-RE safe code around field expressions.
+   If the expression is an array access, create an offset by multiplying the
+   index access by the __builtin_type_info call requesting the size of the
+   array element and multiplying by the index.  The offset is added to the
+   base pointer.
+   In a more formal way:
+     - base + (__blt_preserve_type_info (typeof(expr), SIZEOF) * array_i)
+
+   Please notice that __builtin_preserve_type_info is never really created, but
+   rather a call to __builtin_core_reloc that represents it.
+
+   Any other case, it is assumed to be a field access and instead a CO-RE field
+   expressions offset relocation is created and added to the base node.
+   More precisely:
+     - base + __builtin_preserve_field_expr (expr, SIZEOF)  */
+
+static tree
+core_expr_with_field_expr_plus_base (tree base, tree expr, bool leaf_node)
+{
+  tree type = TREE_TYPE (expr);
+  tree args[2];
 
-	  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));
+  if (base == expr)
+    return expr;
+  else if (TREE_CODE (expr) == ARRAY_REF
+	   && leaf_node == false)
+    {
+      if (TREE_CODE (base) == MEM_REF)
+	base = TREE_OPERAND (base, 0);
+
+      tree array_index = TREE_OPERAND (expr, 1);
+      tree fndecl = bpf_builtins[BPF_BUILTIN_PRESERVE_TYPE_INFO];
+
+      tree type = TREE_TYPE (base);
+      gcc_assert (POINTER_TYPE_P (type)
+		  && TREE_CODE (type = TREE_TYPE (type)) == ARRAY_TYPE
+		  && (type = TREE_TYPE (type)) != NULL_TREE);
+      args[0] = type;
+      args[1] = build_int_cst (integer_type_node, BPF_TYPE_SIZE);
+      tree builtin_call = construct_builtin_core_reloc (UNKNOWN_LOCATION,
+							fndecl,
+							args, 2);
+
+      tree offset = fold_build2 (MULT_EXPR, size_type_node,
+			fold_build1 (NOP_EXPR, size_type_node, builtin_call),
+			fold_build1 (NOP_EXPR, size_type_node, array_index));
+
+      if (!POINTER_TYPE_P (TREE_TYPE (base)))
+	base = fold_build1 (ADDR_EXPR,
+			    build_pointer_type (TREE_TYPE (base)), base);
+
+      tree tmp = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node,
+			 fold_build1 (NOP_EXPR, ptr_type_node, base),
+			 offset);
+
+      tmp = fold_build1 (NOP_EXPR, build_pointer_type (type), tmp);
+      return tmp;
+    }
+  else
+    {
+      tree fndecl = bpf_builtins[BPF_BUILTIN_PRESERVE_FIELD_INFO];
+      args[0] = expr;
+      args[1] = build_int_cst (integer_type_node, BPF_FIELD_BYTE_OFFSET);
+      tree builtin_call = construct_builtin_core_reloc (UNKNOWN_LOCATION,
+							fndecl,
+							args, 2);
+
+      if (!POINTER_TYPE_P (TREE_TYPE (base)))
+	base = fold_build1 (ADDR_EXPR,
+			    build_pointer_type (TREE_TYPE (base)), base);
+
+      tree tmp = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node,
+			 fold_build1 (NOP_EXPR, ptr_type_node, base),
+			 fold_build1 (NOP_EXPR, size_type_node, builtin_call));
+      tmp = fold_build1 (NOP_EXPR, build_pointer_type (type), tmp);
+      return tmp;
+    }
+}
+
+/* This function takes arbitrary field expression and returns a CO-RE
+   compatible version by introducing CO-RE relocations.
+
+   In cases where the expression is not supported in CO-RE with a single
+   relocation, it creates multiple levels of access, i.e. if the expression
+   contains multiple indirections.
+   For example:
+     - A->B->C
+
+   It recursively traverses the expression to its leaf nodes and rollbacks
+   constructing the CO-RE relocations.  It calls
+   core_expr_with_field_expr_plus_base that creates the necessary CO-RE
+   relocations.
+
+   Arguments:
+    - EXPR: is the expression to be converted.  It should be validated by
+    compute_field_expr function before this function is called.
+    - CHANGED: is set to true if the returned tree node is different from
+    the input expr argument.
+    - ENTRY: internal only.  Should not be set in a call.  */
+
+static tree
+make_core_safe_access_index (tree expr, bool *changed, bool entry = true)
+{
+  poly_int64 bitsize, bitpos;
+  tree var_off;
+  machine_mode mode;
+  int sign, reverse, vol;
+
+  tree base = get_inner_reference (expr, &bitsize, &bitpos, &var_off, &mode,
+				   &sign, &reverse, &vol);
+
+  if (base == NULL_TREE || base == expr)
+    return expr;
+
+  tree ret = NULL_TREE;
+  int n;
+  bool valid = true;
+  tree access_node = NULL_TREE;
+
+  /* In case the base is itself a valid field expression, first convert the
+     base in a CO-RE safe expression.
+     This seems to be a requirement since get_inner_reference not always
+     returns the true base of the expression.  */
+  if ((n = compute_field_expr (base, NULL, &valid, &access_node)) > 0
+      && valid == true)
+    {
+      if (TREE_CODE (access_node) == INDIRECT_REF)
+	base = TREE_OPERAND (access_node, 0);
+
+      bool local_changed = false;
+      ret = make_core_safe_access_index (base, &local_changed, false);
+      if (local_changed == true)
+	{
+	  if (TREE_CODE (access_node) == INDIRECT_REF)
+	    base = fold_build1 (INDIRECT_REF,
+				TREE_TYPE (base),
+				ret);
+	  else
+	    base = ret;
+	}
+    }
+
+  /* The remaining is to traverse the field part of the field expression.  */
+  if (mode != VOIDmode && var_off == NULL_TREE)
+    {
+      *changed = true;
+      return core_expr_with_field_expr_plus_base (base, expr, true);
+    }
+  else
+    {
+      switch (TREE_CODE (expr))
+	{
+	case COMPONENT_REF:
+	case ARRAY_REF:
+	case ADDR_EXPR:
+	  {
+	    bool local_changed = false;
+	    tree type = TREE_TYPE (TREE_OPERAND (expr, 0));
+	    ret = make_core_safe_access_index (TREE_OPERAND (expr, 0),
+					       &local_changed, false);
+
+	    /* This variable is a replaced in the expr for algorithmic purposes.
+	       It reduces the expression just to the remaining sub-expression
+	       that still was not processed.  */
+	    if (local_changed == true)
+	      TREE_OPERAND (expr, 0) = create_tmp_var (type, "fake");
+	  }
+	  break;
+	default:
+	  ret = expr;
+	  break;
+	}
+    }
+
+  base = get_inner_reference (expr, &bitsize, &bitpos, &var_off, &mode,
+			      &sign, &reverse, &vol);
+
+  if ((ret != NULL_TREE
+       && mode != VOIDmode && var_off != NULL_TREE)
+      || entry == true)
+    {
+      *changed = true;
+      ret = core_expr_with_field_expr_plus_base (ret, expr, false);
+    }
+  return ret;
+}
+
+/* This function verifies if the NODE expression is a field expression and
+   changes and converts it to CO-RE.  This is used by a tree walker for any
+   __builtin_preserve_access_index argument expression from within
+   bpf_resolve_overloaded_core_builtin.  */
+
+static tree
+replace_core_access_index_comp_expr (tree *node, int *walk_subtrees,
+				     void *data ATTRIBUTE_UNUSED)
+{
+  bool valid = true;
+  gcc_assert (*node != NULL_TREE);
+
+  tree *expr = node;
+  bool should_indirect = false;
+  if (TREE_CODE (*expr) == ADDR_EXPR)
+    expr = &TREE_OPERAND (*expr, 0);
+  else
+    should_indirect = true;
+
+  int n = compute_field_expr (*node, NULL, &valid, NULL);
+  if (valid == true && n > 0)
+    {
+      bool changed = false;
+      tree expr_test = make_core_safe_access_index (*expr, &changed);
+      *walk_subtrees = 0;
+
+      if (changed == true)
+	{
+	  if (should_indirect == true)
+	    expr_test = fold_build1 (INDIRECT_REF,
+				     TREE_TYPE (TREE_TYPE (expr_test)),
+				     expr_test);
+
+	  *expr = expr_test;
 	}
     }
   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.  */
+/* This function is used by bpf_resolve_overloaded_builtin defined in bpf.cc.
+   It is executed in a very early stage and processes any CO-RE builtins,
+   adapting the code and creating the more generic __builtin_core_reloc calls.
+   */
 
 #define MAX_CORE_BUILTIN_ARGS 3
 tree
 bpf_resolve_overloaded_core_builtin (location_t loc, tree fndecl,
 				     void *arglist)
 {
+  remove_parser_plugin ();
+
   if (!bpf_require_core_support ())
     return error_mark_node;
 
@@ -1255,29 +1483,43 @@  bpf_resolve_overloaded_core_builtin (location_t loc, tree fndecl,
   for (unsigned int i = 0; i < argsvec->length (); i++)
     args[i] = (*argsvec)[i];
 
-  remove_parser_plugin ();
+  int code = DECL_MD_FUNCTION_CODE (fndecl);
+  if (code == BPF_BUILTIN_PRESERVE_ACCESS_INDEX)
+  {
+    walk_tree (&args[0], replace_core_access_index_comp_expr, NULL, NULL);
+    return args[0];
+  }
 
   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.  */
+   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)
+  if (!TARGET_BPF_CORE)
+    return NULL_RTX;
+
+  switch (code)
     {
-      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);
+    case 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);
+      }
+      break;
+    default:
+      break;
     }
 
   return NULL_RTX;
@@ -1287,8 +1529,8 @@  bpf_expand_core_builtin (tree exp, enum bpf_builtins code)
    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.  */
+   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)
@@ -1306,67 +1548,198 @@  bpf_add_core_reloc (rtx *operands, const char *templ)
   make_core_relo (&reloc_data, tmp_label);
 
   /* Replace default value for later processing builtin types.
-     Example if the type id builtins. */
+     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.  */
+static tree
+maybe_get_base_for_field_expr (tree expr)
+{
+  poly_int64 bitsize, bitpos;
+  tree var_off;
+  machine_mode mode;
+  int sign, reverse, vol;
 
-void
-bpf_replace_core_move_operands (rtx *operands)
+  if (expr == NULL_TREE)
+    return NULL_TREE;
+
+  return get_inner_reference (expr, &bitsize, &bitpos, &var_off, &mode,
+			      &sign, &reverse, &vol);
+}
+
+/* Access functions to mark sub expressions as attributed with
+   __preserve_access_index.
+   This is required since in gimple format, in order to convert an expression as
+   CO-RE safe, we must create multiple gimple statements.
+   Also, only the type of the base of the expression might be attributed with
+   __preserve_access_index.  Nevertheless all the consecutive accesses to this
+   attributed node should also be converted to CO-RE safe.
+   Any LHS assigned values with CO-RE converted expressions are marked and
+   any uses of these values are later checked for further convertion.
+   The core_access_index_map functions allow to mark this nodes for later
+   convertion to CO-RE.
+   This mechanism are used by make_gimple_core_safe_access_index.  */
+
+static GTY(()) hash_map<tree, tree> *core_access_index_map = NULL;
+
+static void
+core_access_clean (void)
 {
-  for (int i = 0; i < 2; i++)
-    if (MEM_P (operands[i]))
-      {
-	tree expr = MEM_EXPR (operands[i]);
+  if (core_access_index_map == NULL)
+    core_access_index_map = hash_map<tree, tree>::create_ggc (10);
+  core_access_index_map->empty ();
+}
 
-	if (expr == NULL_TREE)
-	  continue;
+static bool
+core_is_access_index (tree expr)
+{
+  if (TREE_CODE (expr) == MEM_REF
+      || TREE_CODE (expr) == INDIRECT_REF)
+    expr = TREE_OPERAND (expr, 0);
 
-	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);
+  tree *def = core_access_index_map->get (expr);
+  if (def)
+    return true;
+  return false;
+}
 
-	    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;
+static void
+core_mark_as_access_index (tree expr)
+{
+  if (TREE_CODE (expr) == MEM_REF
+      || TREE_CODE (expr) == INDIRECT_REF)
+    expr = TREE_OPERAND (expr, 0);
 
-	    int index = allocate_builtin_data ();
-	    struct cr_builtins *data = get_builtin_data (index);
-	    memcpy (data, &local_data.reloc_data, sizeof (struct cr_builtins));
+  if (bpf_enum_mappings->get (expr) == NULL)
+    core_access_index_map->put (expr, NULL_TREE);
+}
 
-	    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);
-	      }
+/* This function is an adaptation of make_core_safe_access_index but to be used
+   in gimple format trees.  It is used by execute_lower_bpf_core, when
+   traversing the gimple tree looking for nodes that would have its type
+   attributed with __preserve_access_index.  In this particular cases any of
+   the expressions using such attributed types must be made CO-RE safe.  */
 
-	    emit_insn (
-	      gen_mov_reloc_coredi (reg,
-				    gen_rtx_CONST_INT (Pmode, 0),
-				    gen_rtx_CONST_INT (Pmode, index)));
-	    return;
-	  }
-      }
+static tree
+make_gimple_core_safe_access_index (tree *tp,
+				    int *walk_subtrees ATTRIBUTE_UNUSED,
+				    void *data)
+{
+  struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+  bool valid = true;
+  int n = 0;
+
+  tree *patch = tp;
+  if (TREE_CODE (*patch) == ADDR_EXPR)
+    patch = &(TREE_OPERAND (*tp, 0));
+  tree orig_type = TREE_TYPE (*patch);
+
+  if ((is_attr_preserve_access (*patch)
+      || core_is_access_index (maybe_get_base_for_field_expr (*patch)))
+      && (n = compute_field_expr (*patch, NULL, &valid, NULL)) > 0
+      && valid == true)
+    {
+      bool changed = false;
+      tree expr_test = make_core_safe_access_index (*patch, &changed);
+
+
+      gimple_seq before = NULL;
+      push_gimplify_context ();
+      gimplify_expr (&expr_test, &before, NULL, is_gimple_val, fb_rvalue);
+
+      /* In case the ADDR_EXPR bypassed above is no longer needed.  */
+      if (patch != tp && TREE_TYPE (expr_test) == TREE_TYPE (*tp))
+	*tp = expr_test;
+      /* For non pointer value accesses.  */
+      else if (TREE_TYPE (expr_test) == build_pointer_type (orig_type))
+	*patch = fold_build2 (MEM_REF, TREE_TYPE (*patch),
+			      expr_test, build_int_cst (ptr_type_node, 0));
+      else
+	*patch = expr_test;
+
+      *tp = fold (*tp);
+
+      gsi_insert_seq_before (&(wi->gsi), before, GSI_LAST_NEW_STMT);
+      pop_gimplify_context (NULL);
+
+      wi->changed = true;
+      *walk_subtrees = false;
+
+      tree lhs;
+      if (!wi->is_lhs
+	  && (lhs = gimple_get_lhs (wi->stmt)) != NULL_TREE)
+	core_mark_as_access_index (lhs);
+    }
+  return NULL_TREE;
+}
+
+/* This is the entry point for the pass_data_lower_bpg_core.  It walks all the
+   statements in gimple, looking for expressions that are suppose to be CO-RE
+   preserve_access_index attributed.
+   Those expressions are processed and split by
+   make_gimple_core_safe_access_index function, which will both create the
+   calls to __build_core_reloc and split the expression in smaller parts in
+   case it cannot be represented CO-RE safeguarded by a single CO-RE
+   relocation.  */
+
+static unsigned int
+execute_lower_bpf_core (void)
+{
+  if (!TARGET_BPF_CORE)
+    return 0;
+
+  gimple_seq body = gimple_body (current_function_decl);
+
+  struct walk_stmt_info wi;
+  core_access_clean ();
+
+  memset (&wi, 0, sizeof (wi));
+  wi.info = NULL;
+
+  /* Split preserve_access_index expressions when needed.  */
+  walk_gimple_seq_mod (&body, NULL, make_gimple_core_safe_access_index, &wi);
+  return 0;
+}
+
+namespace {
+
+const pass_data pass_data_lower_bpf_core =
+{
+  GIMPLE_PASS, /* type */
+  "bpf_core_lower", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_gimple_any, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_lower_bpf_core: public gimple_opt_pass
+{
+public:
+  pass_lower_bpf_core (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_lower_bpf_core, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  unsigned int execute (function *) final override
+  {
+    return execute_lower_bpf_core ();
+  }
+
+}; /* class pass_lower_bpf_core */
+
+} /* anon namespace */
+
+gimple_opt_pass *
+make_pass_lower_bpf_core (gcc::context *ctxt)
+{
+  return new pass_lower_bpf_core (ctxt);
 }
 
 #include "gt-core-builtins.h"
diff --git a/gcc/config/bpf/core-builtins.h b/gcc/config/bpf/core-builtins.h
index 15cd3d3a94e5..c54f6ddac812 100644
--- a/gcc/config/bpf/core-builtins.h
+++ b/gcc/config/bpf/core-builtins.h
@@ -25,6 +25,34 @@  enum bpf_builtins
   BPF_BUILTIN_MAX,
 };
 
+enum bpf_field_info_kind {
+  BPF_FIELD_BYTE_OFFSET = 0,	/* field byte offset */
+  BPF_FIELD_BYTE_SIZE = 1,
+  BPF_FIELD_EXISTS = 2,		/* field existence in target kernel */
+  BPF_FIELD_SIGNED = 3,
+  BPF_FIELD_LSHIFT_U64 = 4,
+  BPF_FIELD_RSHIFT_U64 = 5,
+};
+
+/* second argument to __builtin_btf_type_id () built-in */
+enum bpf_type_id_kind {
+  BPF_TYPE_ID_LOCAL = 0,		/* BTF type ID in local program */
+  BPF_TYPE_ID_TARGET = 1,		/* BTF type ID in target kernel */
+};
+
+/* second argument to __builtin_preserve_type_info () built-in */
+enum bpf_type_info_kind {
+  BPF_TYPE_EXISTS = 0,		/* type existence in target kernel */
+  BPF_TYPE_SIZE = 1,		/* type size in target kernel */
+  BPF_TYPE_MATCHES = 2,		/* type match in target kernel */
+};
+
+/* second argument to __builtin_preserve_enum_value () built-in */
+enum bpf_enum_value_kind {
+  BPF_ENUMVAL_EXISTS = 0,		/* enum value existence in kernel */
+  BPF_ENUMVAL_VALUE = 1,		/* enum value value relocation */
+};
+
 extern GTY (()) tree bpf_builtins[(int) BPF_BUILTIN_MAX];
 
 void bpf_init_core_builtins (void);
diff --git a/gcc/config/bpf/t-bpf b/gcc/config/bpf/t-bpf
index c289dde8b173..18f1fa67794d 100644
--- a/gcc/config/bpf/t-bpf
+++ b/gcc/config/bpf/t-bpf
@@ -8,3 +8,5 @@  coreout.o: $(srcdir)/config/bpf/coreout.cc
 core-builtins.o: $(srcdir)/config/bpf/core-builtins.cc
 	$(COMPILE) $<
 	$(POSTCOMPILE)
+
+PASSES_EXTRA += $(srcdir)/config/bpf/bpf-passes.def
diff --git a/gcc/testsuite/gcc.target/bpf/core-attr-5.c b/gcc/testsuite/gcc.target/bpf/core-attr-5.c
new file mode 100644
index 000000000000..c0dc15fbb271
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-attr-5.c
@@ -0,0 +1,62 @@ 
+/* Test for BPF CO-RE __attribute__((preserve_access_index)) with accesses on
+   LHS and both LHS and RHS of assignment.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -dA -gbtf -mco-re" } */
+
+struct U {
+  int c;
+  struct V {
+    int d;
+    int e[4];
+    int f;
+    int *g;
+  } v;
+} __attribute__((preserve_access_index));
+
+struct T {
+  int a;
+  int b;
+  struct U u;
+  struct U *ptr_u;
+  struct U *array_u;
+} __attribute__((preserve_access_index));
+
+
+extern void mset(void *);
+void
+func (struct T *t, int i)
+{
+  /* This next expression is silently generating incomplete CO-RE relocations
+   * because of a front-end optimization to the array access with i.  It is
+   * converting the access to pointer arithmetics, which completely removes the
+   * reference to the type of array_u.  With current folding it is not possible
+   * to generate correct/complete CO-RE relocations for such cases. The XFAIL
+   * is exactly for this reason. Once we do XPASS this test there is a slight
+   * chance we are doing the proper code generation. */
+
+  /* 0:4   sizeof(struct U)   0:1:1:3 */
+  t->array_u[i].v.e[3] = 0xc1;
+
+  /* This was commented since in this case it is detectable as not a field
+   * expression. */
+  /* 0:4   sizeof(struct U)   0:1:1:2 */
+  //__builtin_preserve_access_index (t->array_u[i].v.e[2]) = 0xa1;
+
+  /* 0:3    0:1:1:1   */
+  t->ptr_u->v.e[1] = 0xb1;
+
+  /* 0:3    0:1:2 */
+  mset (&(t->ptr_u->v.f));
+
+  mset (&t->a);
+}
+
+/* { dg-final { scan-assembler-times "ascii \"0:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 2 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct T \\*\\)" 4 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct U \\*\\)" 4 { xfail *-*-* } } } */
+
diff --git a/gcc/testsuite/gcc.target/bpf/core-attr-6.c b/gcc/testsuite/gcc.target/bpf/core-attr-6.c
new file mode 100644
index 000000000000..858ae627cb8b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-attr-6.c
@@ -0,0 +1,46 @@ 
+/* Test for BPF CO-RE __attribute__((preserve_access_index)) with accesses on
+   LHS and both LHS and RHS of assignment.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -dA -gbtf -mco-re" } */
+
+struct U {
+  int c;
+  struct V {
+    int d;
+    int e[4];
+    int f;
+    int *g;
+  } v;
+} u;
+
+struct T {
+  int a;
+  int b;
+  struct U u;
+  struct U *ptr_u;
+  struct U *array_u;
+} __attribute__((preserve_access_index));
+
+extern void mset(void *);
+
+void
+func (struct T *t, int i)
+{
+  /* 0:3    0:1:1:1   */
+  t->ptr_u->v.e[1] = 0xb1;
+
+  /* 0:3    0:1:2 */
+  mset (&(t->ptr_u->v.f));
+
+  /* 0:0 */
+  mset (&t->a);
+}
+
+/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 2 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct T \\*\\)" 3 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct U \\*\\)" 2 } } */
+
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-1.c b/gcc/testsuite/gcc.target/bpf/core-builtin-1.c
index e994dc6add48..3f15980a4c78 100644
--- a/gcc/testsuite/gcc.target/bpf/core-builtin-1.c
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-1.c
@@ -21,7 +21,7 @@  unsigned long ula[8];
 
 #define _(x) (__builtin_preserve_access_index (x))
 
-void
+unsigned long
 func (void)
 {
   /* 1 */
@@ -35,6 +35,8 @@  func (void)
 
   /* 6 */
   unsigned long ul = _(ula[6]);
+
+  return b + c + uc + ul;
 }
 
 char
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-enumvalue-opt.c b/gcc/testsuite/gcc.target/bpf/core-builtin-enumvalue-opt.c
index 363ad0c41ecc..c87e1a3ba3b0 100644
--- a/gcc/testsuite/gcc.target/bpf/core-builtin-enumvalue-opt.c
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-enumvalue-opt.c
@@ -26,8 +26,8 @@  unsigned long foo(void *data)
  return 0;
 }
 
-/* { dg-final { scan-assembler-times "\t.4byte\t0x8\t; bpfcr_type \\(named_ue64\\)" 2 } } */
-/* { dg-final { scan-assembler-times "\t.4byte\t0x9\t; bpfcr_type \\(named_se64\\)" 2} } */
+/* { dg-final { scan-assembler-times "\t.4byte\t0x\[0-9a-f\]+\t; bpfcr_type \\(named_ue64\\)" 2 } } */
+/* { dg-final { scan-assembler-times "\t.4byte\t0x\[0-9a-f\]+\t; bpfcr_type \\(named_se64\\)" 2} } */
 /* { dg-final { scan-assembler-times "\t.4byte\t0xa\t; bpfcr_kind" 2 } } BPF_ENUMVAL_EXISTS */
 /* { dg-final { scan-assembler-times "\t.4byte\t0xb\t; bpfcr_kind" 2 } } BPF_ENUMVAL_VALUE */
 
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-enumvalue.c b/gcc/testsuite/gcc.target/bpf/core-builtin-enumvalue.c
index 3e3334dc089a..2f16903b8d64 100644
--- a/gcc/testsuite/gcc.target/bpf/core-builtin-enumvalue.c
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-enumvalue.c
@@ -40,10 +40,10 @@  int foo(void *data)
  return 0;
 }
 
-/* { dg-final { scan-assembler-times "\t.4byte\t0x8\t; bpfcr_type \\(named_ue64\\)" 5 } } */
-/* { dg-final { scan-assembler-times "\t.4byte\t0x9\t; bpfcr_type \\(named_se64\\)" 5} } */
-/* { dg-final { scan-assembler-times "\t.4byte\t0xb\t; bpfcr_type \\(named_ue\\)" 5 } } */
-/* { dg-final { scan-assembler-times "\t.4byte\t0xc\t; bpfcr_type \\(named_se\\)" 5} } */
+/* { dg-final { scan-assembler-times "\t.4byte\t0x\[0-9a-f\]+\t; bpfcr_type \\(named_ue64\\)" 5 } } */
+/* { dg-final { scan-assembler-times "\t.4byte\t0x\[0-9a-f\]+\t; bpfcr_type \\(named_se64\\)" 5} } */
+/* { dg-final { scan-assembler-times "\t.4byte\t0x\[0-9a-f\]+\t; bpfcr_type \\(named_ue\\)" 5 } } */
+/* { dg-final { scan-assembler-times "\t.4byte\t0x\[0-9a-f\]+\t; bpfcr_type \\(named_se\\)" 5} } */
 /* { dg-final { scan-assembler-times "\t.4byte\t0xa\t; bpfcr_kind" 12 } } BPF_ENUMVAL_EXISTS */
 /* { dg-final { scan-assembler-times "\t.4byte\t0xb\t; bpfcr_kind" 8 } } BPF_ENUMVAL_VALUE */
 
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-1.c b/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-1.c
new file mode 100644
index 000000000000..b8bdeeaa125c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-1.c
@@ -0,0 +1,76 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct S {
+  int a;
+  int b;
+  char c;
+};
+
+union U {
+  unsigned int u;
+  int i;
+  unsigned char uc[4];
+  signed char ic[4];
+};
+
+struct S my_s;
+union U my_u;
+
+unsigned long ula[8];
+
+#define _(x) (__builtin_preserve_access_index (x))
+
+unsigned long
+func (void)
+{
+  int b;
+  char c;
+  unsigned char uc;
+  unsigned long ul;
+  int ic;
+
+  __builtin_preserve_access_index (({
+    /* 1 */
+    b = my_s.b;
+
+    /* 2 */
+    ic = my_s.c;
+
+    /* 2:3 */
+    uc = my_u.uc[3];
+
+    /* 6 */
+    ul = ula[6];
+  }));
+
+  return b + ic + uc + ul;
+}
+
+char
+s_ptr (struct S *ps)
+{
+  /* 0:2 */
+  char x;
+  __builtin_preserve_access_index (({ x = ps->c; }));
+  return x;
+}
+
+unsigned char
+u_ptr (union U *pu)
+{
+  /* 0:2:3 */
+  unsigned char x;
+  __builtin_preserve_access_index (({ x = pu->uc[3]; }));
+  return x;
+}
+
+/* { dg-final { scan-assembler-times "ascii \"1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"2:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"6.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "bpfcr_type" 6 } } */
+/* { dg-final { scan-assembler-times "\[\t \]0x6c\[\t \]+\[^\n\]*core_relo_len" 1 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-2.c b/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-2.c
new file mode 100644
index 000000000000..3a22b99f8e6f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-2.c
@@ -0,0 +1,35 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct T {
+  int a;
+  int b;
+  struct U {
+    int c;
+    struct V {
+      int d;
+      int e[4];
+      int f;
+    } v;
+  } u;
+};
+
+void func (struct T * foo)
+{
+  /* Access string: "0:2:1:1:3" */
+  int *x;
+  int *y;
+  __builtin_preserve_access_index (({
+    x =  &(foo->u.v.e[3]);
+    struct U *tmp = &(foo->u);
+    y =  &(tmp->v.f);
+  }));
+
+  *x = 17;
+  *y = 16;
+}
+
+/* { dg-final { scan-assembler-times "ascii \"0:2:1:1:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type" 3 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-3.c b/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-3.c
new file mode 100644
index 000000000000..bccf2b3a6968
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-3.c
@@ -0,0 +1,37 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct T {
+  int a;
+  int b;
+  struct U {
+    int c;
+    struct V {
+      int d;
+      int e[4];
+      int f;
+    } v;
+  } u;
+};
+
+void func (struct T * foo)
+{
+  /* Access string: "0:2:1:1:3" */
+  int *x;
+  int *y;
+  struct U *tmp;
+  __builtin_preserve_access_index (({
+    x =  &(foo->u.v.e[3]);
+    tmp = &(foo->u);
+  }));
+  y = __builtin_preserve_access_index(tmp ? 0 : &(tmp->v.f));
+
+  *x = 17;
+  if (y != 0)
+    *y = 16;
+}
+
+/* { dg-final { scan-assembler-times "ascii \"0:2:1:1:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type" 3 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-4.c b/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-4.c
new file mode 100644
index 000000000000..8ef239c30c15
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-exprlist-4.c
@@ -0,0 +1,31 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0 -dA -gbtf -mco-re" } */
+
+struct T {
+  int a;
+  int b;
+  struct U {
+    int c;
+    struct V {
+      int d;
+      int e[4];
+      int f;
+    } *v;
+  } u;
+};
+
+void func (struct T * foo)
+{
+  /* Access string: "0:2:1:1:3" */
+  int *x;
+  int *y;
+  __builtin_preserve_access_index (({
+    x =  &((foo->u.v)->e[3]);
+  }));
+
+  *x = 17;
+}
+
+/* { dg-final { scan-assembler-times "ascii \"0:2:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type" 2 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-offset-1.c b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-offset-1.c
index 590eea007ae1..a4af9a53282a 100644
--- a/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-offset-1.c
+++ b/gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-offset-1.c
@@ -20,6 +20,7 @@  enum {
   FIELD_BYTE_OFFSET = 0,
 };
 
+extern struct T *bar();
 
 unsigned int foo (struct T *t)
 {
@@ -34,23 +35,26 @@  unsigned int foo (struct T *t)
   unsigned c = __builtin_preserve_field_info (t->c, FIELD_BYTE_OFFSET);
   unsigned d = __builtin_preserve_field_info (t->d, FIELD_BYTE_OFFSET);
 
-  return s0a1 + s0a4 + s0x + s1a1 + s1a4 + s1x + c + d;
+  unsigned e1 = __builtin_preserve_field_info (bar()->d, FIELD_BYTE_OFFSET);
+  unsigned e2 = __builtin_preserve_field_info (bar()->s[1].a4, FIELD_BYTE_OFFSET);
+
+  return s0a1 + s0a4 + s0x + s1a1 + s1a4 + s1x + c + d + e1 + e2;
 }
 
 /* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],4" 2 } } */
 /* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],8" 1 } } */
-/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],12" 2 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],12" 3 } } */
 /* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],16" 1 } } */
 /* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],20" 1 } } */
-/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],21" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],21" 2 } } */
 
 /* { dg-final { scan-assembler-times "ascii \"0:1:0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
 /* { dg-final { scan-assembler-times "ascii \"0:1:0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
 /* { dg-final { scan-assembler-times "ascii \"0:1:0:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
 /* { dg-final { scan-assembler-times "ascii \"0:1:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
-/* { dg-final { scan-assembler-times "ascii \"0:1:1:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:1:1:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 2 } } */
 /* { dg-final { scan-assembler-times "ascii \"0:1:1:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
 /* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
-/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 2 } } */
 
-/* { dg-final { scan-assembler-times "0\[\t \]+\[^\n\]*bpfcr_kind" 8 } } */
+/* { dg-final { scan-assembler-times "0\[\t \]+\[^\n\]*bpfcr_kind" 10 } } */