Hi everyone,

This is v4. This patch goes one step further and simplifies how the
expressions are converted to CO-RE in the case where
explicit expressions should be converted, i.e. no
builtin_preserve_access_index is used.
Since the expression split separates CO-RE expressions from non CO-RE
expressions it also allowed to simplify the walker that creates the
internal core_builtin calls.

This is a delta between the before and after of the patch.

# diff /tmp/baseline.txt /tmp/new.txt 
155c155
< #35/15   bpf_tcp_ca/cc_cubic:FAIL
---
> #35/15   bpf_tcp_ca/cc_cubic:OK
157c157
< #35      bpf_tcp_ca:FAIL
---
> #35      bpf_tcp_ca:OK
1166c1166
< #109/5   fd_array_cnt/fd-array-ref-btfs:FAIL
---
> #109/5   fd_array_cnt/fd-array-ref-btfs:OK
1169c1169
< #109     fd_array_cnt:FAIL
---
> #109     fd_array_cnt:OK
1686c1686
< #189/1   lsm_cgroup/functional:FAIL
---
> #189/1   lsm_cgroup/functional:OK
1688c1688
< #189     lsm_cgroup:FAIL
---
> #189     lsm_cgroup:OK
4155,4159c4155,4159
< #546/1   verifier_bitfield_write/single CO-RE bitfield roundtrip:FAIL
< #546/2   verifier_bitfield_write/multiple CO-RE bitfield roundtrip:FAIL
< #546/3   verifier_bitfield_write/adjacent CO-RE bitfield roundtrip:FAIL
< #546/4   verifier_bitfield_write/multibyte CO-RE bitfield roundtrip:FAIL
< #546     verifier_bitfield_write:FAIL
---
> #546/1   verifier_bitfield_write/single CO-RE bitfield roundtrip:OK
> #546/2   verifier_bitfield_write/multiple CO-RE bitfield roundtrip:OK
> #546/3   verifier_bitfield_write/adjacent CO-RE bitfield roundtrip:OK
> #546/4   verifier_bitfield_write/multibyte CO-RE bitfield roundtrip:OK
> #546     verifier_bitfield_write:OK

Looking forward to your review.

Thanks,
Cupertino

---------

This patch corrects CO-RE generation for the cases where an expression
starts with a non-CO-RE access, but in the middle it requires to
generate CO-RE to correctly compute the access location.

It fixes it by splitting the expression into their CO-RE and non-CO-RE
counterparts. It performs this by walking gimple expressions, and for
each field access to which its type is a struct or union, it verifies if
both the types for the base and field are attributed similarly.
Otherwise, it splits the expression at this location by creating a
temporary variable and performing any required pointer conversions.
This smaller expressions are converted to CO-RE in the subsequent
gimple walker.

There is no way in GCC to distinguish nested struct/union definitions
from non-nested ones.
This patch simplifies code and enforces that all preserve_access_index
structs/unions would be attributed explicitly.
Previous approach was error prone as it would extend CO-RE accesses to
structures which would not be attributed.

All GCC BPF dejagnu passes tests:
  # of expected passes          553
  # of expected failures                6

kernel-next bpf selftests:
 before: Summary: 543/4888 PASSED, 113 SKIPPED, 136 FAILED
 after:  Summary: 545/4893 PASSED, 113 SKIPPED, 134 FAILED

gcc/ChangeLog:
        PR target/120241
        * config/bpf/core-builtins.cc
        (is_attr_preserve_access): Correct for pointer types.
        (maybe_get_base_for_field_expr, core_access_index_map,
        core_access_clean, core_is_access_index, core_mark_as_access_index):
        Remove.
        (make_gimple_core_safe_access_index): Remove function.
        (struct walker_data): New struct to pass data to tree walker.
        (callback_should_do_core_access, should_do_core_access): Add
        function to identify expressions that should not be converted to
        CO-RE.
        (core_make_builtins): Add callback tree walker function to
        convert expressions to CO-RE.
        (callback_find_next_split_location, core_should_split_expr,
        find_next_split_location, gimple_core_early_split_expr): Add
        function to split expressions in CO-RE and non-CO-RE
        expressions.
        (execute_lower_bpf_core): Adapt to new code.

gcc/testsuite/ChangeLog:
        PR target/120241
        * gcc.target/bpf/core-attr-3.c: Add attribute.
        * gcc.target/bpf/core-attr-4.c: Add attribute.
        * gcc.target/bpf/core-attr-5.c: Add attribute.
        * gcc.target/bpf/core-attr-6.c: Add attribute.
        * gcc.target/bpf/core-attr-7.c: New test.
        * gcc.target/bpf/core-attr-calls.c: Adapt.
---
 gcc/config/bpf/bpf.opt                        |   4 +
 gcc/config/bpf/core-builtins.cc               | 306 ++++++++++++------
 gcc/testsuite/gcc.target/bpf/core-attr-3.c    |   4 +-
 gcc/testsuite/gcc.target/bpf/core-attr-4.c    |   4 +-
 gcc/testsuite/gcc.target/bpf/core-attr-5.c    |   2 +-
 gcc/testsuite/gcc.target/bpf/core-attr-6.c    |   4 +-
 gcc/testsuite/gcc.target/bpf/core-attr-7.c    | 150 +++++++++
 .../gcc.target/bpf/core-attr-calls.c          |   4 +-
 8 files changed, 371 insertions(+), 107 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-attr-7.c

diff --git a/gcc/config/bpf/bpf.opt b/gcc/config/bpf/bpf.opt
index 54d038a7fdea..fe34b18c19d6 100644
--- a/gcc/config/bpf/bpf.opt
+++ b/gcc/config/bpf/bpf.opt
@@ -112,3 +112,7 @@ Enum(asm_dialect) String(pseudoc) Value(ASM_PSEUDOC)
 minline-memops-threshold=
 Target RejectNegative Joined UInteger Var(bpf_inline_memops_threshold) 
Init(1024)
 -minline-memops-threshold=<number> Maximum size of memset/memmove/memcpy to 
inline, larger sizes will use a library call.
+
+Wbpf-core
+Target Warning
+Warn when CO-RE support is not guaranteed.
diff --git a/gcc/config/bpf/core-builtins.cc b/gcc/config/bpf/core-builtins.cc
index 152da94a9761..cf32fe08a3f5 100644
--- a/gcc/config/bpf/core-builtins.cc
+++ b/gcc/config/bpf/core-builtins.cc
@@ -324,8 +324,13 @@ is_attr_preserve_access (tree t)
                             TYPE_ATTRIBUTES (TREE_TYPE (base)));
 
   if (TREE_CODE (base) == MEM_REF)
-    return lookup_attribute ("preserve_access_index",
-                            TYPE_ATTRIBUTES (TREE_TYPE (base)));
+    {
+      tree type = TREE_TYPE (base);
+      if (POINTER_TYPE_P (type))
+       type = TREE_TYPE (type);
+      return lookup_attribute ("preserve_access_index",
+                              TYPE_ATTRIBUTES (type));
+    }
 
   if (TREE_CODE (t) == COMPONENT_REF)
     {
@@ -1710,137 +1715,241 @@ bpf_output_core_reloc (rtx *operands, int nr_ops)
     }
 }
 
+/* This function verifies if the passed tree node t is a struct field
+   expression and if at this location is is necessary to break the expression
+   in order to split CO-RE and non CO-RE required field/union accesses.  */
+
+static bool
+core_should_split_expr (tree t)
+{
+  tree type = TREE_TYPE (t);
+  if (TREE_CODE (type) != RECORD_TYPE
+      && TREE_CODE (type) != UNION_TYPE)
+    return false;
+
+  if (TREE_CODE (t) == COMPONENT_REF)
+    {
+      tree base = TREE_OPERAND (t, 0);
+
+      bool base_is_attr = lookup_attribute ("preserve_access_index",
+                            TYPE_ATTRIBUTES (TREE_TYPE (base))) != NULL_TREE;
+      bool cur_is_attr = lookup_attribute ("preserve_access_index",
+                            TYPE_ATTRIBUTES (TREE_TYPE (t))) != NULL_TREE;
+
+      if (base_is_attr != cur_is_attr)
+       return true;
+    }
+  return false;
+}
+
+struct walker_data {
+  bool is_root;
+  tree *core_base_pointer;
+};
+
 static tree
-maybe_get_base_for_field_expr (tree expr)
+callback_find_next_split_location (tree *tp,
+                                  int *walk_subtrees ATTRIBUTE_UNUSED,
+                                  void *data)
 {
-  poly_int64 bitsize, bitpos;
-  tree var_off;
-  machine_mode mode;
-  int sign, reverse, vol;
+  struct walker_data *d = (struct walker_data *) data;
 
-  if (expr == NULL_TREE)
+  if (d->is_root == true && TREE_CODE (*tp) == ADDR_EXPR)
     return NULL_TREE;
 
-  return get_inner_reference (expr, &bitsize, &bitpos, &var_off, &mode,
-                             &sign, &reverse, &vol);
+  if (d->is_root == false && core_should_split_expr (*tp))
+    {
+      d->core_base_pointer = tp;
+      return *tp;
+    }
+  else
+    {
+      /* Keep walking the expression.  */
+      d->is_root = false;
+      return NULL_TREE;
+    }
 }
 
-/* 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;
+/* Walk an expression and return the node at any location in the expression
+   where we should break the expression in its CO-RE and non CO-RE parts.  */
 
-static void
-core_access_clean (void)
+static tree *
+find_next_split_location (tree *tp, bool is_first)
 {
-  if (core_access_index_map == NULL)
-    core_access_index_map = hash_map<tree, tree>::create_ggc (10);
-  core_access_index_map->empty ();
+  struct walker_data data = { is_first, NULL };
+  walk_tree (tp, callback_find_next_split_location, &data, NULL);
+  return data.core_base_pointer;
 }
 
-static bool
-core_is_access_index (tree expr)
+
+static tree
+callback_should_do_core_access (tree *tp,
+                               int *walk_subtrees ATTRIBUTE_UNUSED,
+                               void *data ATTRIBUTE_UNUSED)
 {
-  if (TREE_CODE (expr) == MEM_REF
-      || TREE_CODE (expr) == INDIRECT_REF)
-    expr = TREE_OPERAND (expr, 0);
+/* TODO NOTE: This might have to be removed later as it is an exception
+   to mimic clang behavior.  */
+  if (lookup_attribute ("packed", TYPE_ATTRIBUTES (TREE_TYPE (*tp))) != 
NULL_TREE)
+  {
+    warning_at (EXPR_LOC_OR_LOC (*tp, UNKNOWN_LOCATION), OPT_Wbpf_core,
+         "CO-RE expression not guaranteed, struct attribute packed with "
+         "preserve-access-index attributed struct childs");
+    return *tp;
+  }
+  return NULL_TREE;
+}
 
-  tree *def = core_access_index_map->get (expr);
-  if (def)
-    return true;
-  return false;
+
+static bool
+should_do_core_access (tree *tp)
+{
+  tree tfail = walk_tree (tp, callback_should_do_core_access, NULL, NULL);
+  return tfail == NULL_TREE;
 }
 
-static void
-core_mark_as_access_index (tree expr)
+/* This is the main callback function to the gimple traversal that will split
+   any field/union accesses in their CO-RE and non CO-RE parts.
+   An early version of this callback would allow also allow its walker function
+   to traverse the parent tree expression on any initial expression (children
+   tree node), by not setting walk_subtrees to false. In current version only
+   the main tree node of an expression is reached by the traversal function
+   that uses this callback. The sub traversal of the main tree nodes is done in
+   find_next_split_location call.  The function iterates on the parent
+   expressions within the loop that call find_next_split_location function.  */
+
+static tree
+gimple_core_early_split_expr (tree *tp,
+                             int *walk_subtrees,
+                             void *data)
 {
-  if (TREE_CODE (expr) == MEM_REF
-      || TREE_CODE (expr) == INDIRECT_REF)
-    expr = TREE_OPERAND (expr, 0);
+  struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+  tree *split_loc = tp;
 
-  if (core_access_index_map->get (expr) == NULL)
-    core_access_index_map->put (expr, NULL_TREE);
-}
+  if (!should_do_core_access (tp))
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  /* Find the next split location within expression tree.  */
+  tree *expr = NULL;
+  bool is_first = true;
+  while ((expr = find_next_split_location (split_loc, is_first)) != NULL)
+  {
+    gimple_seq before = NULL;
+
+    if (*expr == *split_loc && is_first == true)
+      break;
+    is_first = false;
 
-/* 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.  */
+    push_gimplify_context ();
+    split_loc = &TREE_OPERAND (*expr, 0);
+
+    tree rhs = *expr;
+    bool pointer_base = false;
+    if (!POINTER_TYPE_P (TREE_TYPE (rhs)))
+      {
+       rhs = fold_build1 (ADDR_EXPR,
+                          build_pointer_type (TREE_TYPE (*expr)),
+                          *expr);
+       pointer_base = true;
+      }
+    tree lhs = create_tmp_var_raw (TREE_TYPE (rhs), "core_split");
+    gimple_add_tmp_var (lhs);
+    gimplify_assign (lhs, rhs, &before);
+
+    if (pointer_base == true)
+      lhs = fold_build2 (MEM_REF, TREE_TYPE (lhs), lhs,
+                        build_int_cst (ptr_type_node, 0));
+    *expr = lhs;
+
+    gsi_insert_seq_before (&(wi->gsi), before, GSI_NEW_STMT);
+    pop_gimplify_context (NULL);
+  }
+
+  /* Never traverse subtrees, as find_next_split_location already does it. */
+  *walk_subtrees = false;
+
+  /* Keep traverse all the other tree expressions in gimple. */
+  return NULL_TREE;
+}
 
 static tree
-make_gimple_core_safe_access_index (tree *tp,
-                                   int *walk_subtrees ATTRIBUTE_UNUSED,
-                                   void *data)
+core_make_builtins (tree *tp, int *walk_subtrees, void *data)
 {
   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
-  bool valid = true;
   int n = 0;
+  bool valid = true;
+  tree expr = *tp;
+  bool is_addr_expr = false;
 
-  tree *patch = tp;
-  if (TREE_CODE (*patch) == ADDR_EXPR)
-    patch = &(TREE_OPERAND (*tp, 0));
-  tree orig_type = TREE_TYPE (*patch);
+  if (!should_do_core_access (tp))
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
 
-  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
+  /* Skip ADDR_EXPR node since we will convert to pointer anyway.  */
+  if (TREE_CODE (*tp) == ADDR_EXPR)
+    {
+      is_addr_expr = true;
+      expr = TREE_OPERAND (*tp, 0);
+    }
+
+  if (is_attr_preserve_access (expr)
+      && (n = compute_field_expr (expr, NULL, &valid, NULL)) > 0
       && valid == true)
     {
-      bool changed = false;
-      tree expr_test = make_core_safe_access_index (*patch, &changed);
+      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);
 
-      gimple_seq before = NULL;
-      push_gimplify_context ();
-      gimplify_expr (&expr_test, &before, NULL, is_gimple_val, fb_rvalue);
+      tree new_expr = core_expr_with_field_expr_plus_base (base, expr, true);
 
-      /* 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;
+      /* Abort convertion if it is just a pointer or a reference to an
+        attributed type.  */
+      if (new_expr != expr)
+       {
 
-      *tp = fold (*tp);
+         gimple_seq before = NULL;
+         push_gimplify_context ();
 
-      gsi_insert_seq_before (&(wi->gsi), before, GSI_LAST_NEW_STMT);
-      pop_gimplify_context (NULL);
+         gimplify_expr (&new_expr, &before, NULL, is_gimple_val, fb_rvalue);
 
-      wi->changed = true;
-      *walk_subtrees = false;
+         tree new_expr_type = TREE_TYPE (new_expr);
+
+         /* Replace original expression by new_expr. If the type is the same
+          * tree node, good! If it is a pointer type, we need to the type
+          * within the pointer to guarantee it is actually the same.  */
+         if (TREE_TYPE (new_expr) == TREE_TYPE (*tp)
+             || (is_addr_expr
+                 && TREE_TYPE (new_expr_type) == TREE_TYPE (expr)))
+           *tp = fold (new_expr);
+         else if (TREE_TYPE (new_expr) == build_pointer_type (TREE_TYPE (*tp)))
+           *tp = fold_build2 (MEM_REF, TREE_TYPE (*tp),
+                              new_expr, build_int_cst (ptr_type_node, 0));
+         else
+           gcc_assert (0);
 
-      tree lhs;
-      if (!wi->is_lhs
-         && gimple_code (wi->stmt) != GIMPLE_CALL
-         && (lhs = gimple_get_lhs (wi->stmt)) != NULL_TREE)
-       core_mark_as_access_index (lhs);
+         gsi_insert_seq_before (&(wi->gsi), before, GSI_SAME_STMT);
+         pop_gimplify_context (NULL);
+
+         *walk_subtrees = false;
+       }
     }
   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.  */
-
+   preserve_access_index attributed, and should not.
+   It first splits CO-RE expressions from non CO-RE ones.
+   Then the CO-RE expressions are processed by core_make_builtins which will
+   create the required builtins that will result in the CO-RE relocations.  */
 static unsigned int
 execute_lower_bpf_core (void)
 {
@@ -1851,13 +1960,14 @@ execute_lower_bpf_core (void)
   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);
+  /* Early split to guarantee base of expression is a preserve_access_index
+     structure.  */
+  walk_gimple_seq_mod (&body, NULL, gimple_core_early_split_expr, &wi);
+
+  /* Add CO-RE builtins for all expressions that need them.  */
+  walk_gimple_seq_mod (&body, NULL, core_make_builtins, &wi);
   return 0;
 }
 
diff --git a/gcc/testsuite/gcc.target/bpf/core-attr-3.c 
b/gcc/testsuite/gcc.target/bpf/core-attr-3.c
index 12354fc6f86d..58c27fd43bb4 100644
--- a/gcc/testsuite/gcc.target/bpf/core-attr-3.c
+++ b/gcc/testsuite/gcc.target/bpf/core-attr-3.c
@@ -11,14 +11,14 @@
 struct O {
   int e;
   int f;
-};
+} __attribute__((preserve_access_index));
 
 struct S {
   int a;
   struct {
     int b;
     int c;
-  } inner;
+  } __attribute__((preserve_access_index)) inner;
   struct O other;
 } __attribute__((preserve_access_index));
 
diff --git a/gcc/testsuite/gcc.target/bpf/core-attr-4.c 
b/gcc/testsuite/gcc.target/bpf/core-attr-4.c
index 6f025f42f3ee..c001b5b76ef9 100644
--- a/gcc/testsuite/gcc.target/bpf/core-attr-4.c
+++ b/gcc/testsuite/gcc.target/bpf/core-attr-4.c
@@ -13,8 +13,8 @@ struct T {
       int d;
       int e[4];
       int f;
-    } v;
-  } u;
+    } __attribute__((preserve_access_index)) v;
+  } __attribute__((preserve_access_index)) u;
 } __attribute__((preserve_access_index));
 
 
diff --git a/gcc/testsuite/gcc.target/bpf/core-attr-5.c 
b/gcc/testsuite/gcc.target/bpf/core-attr-5.c
index 81e25fa85de7..4d443d88b0ae 100644
--- a/gcc/testsuite/gcc.target/bpf/core-attr-5.c
+++ b/gcc/testsuite/gcc.target/bpf/core-attr-5.c
@@ -11,7 +11,7 @@ struct U {
     int e[4];
     int f;
     int *g;
-  } v;
+  } __attribute__((preserve_access_index)) v;
 } __attribute__((preserve_access_index));
 
 struct T {
diff --git a/gcc/testsuite/gcc.target/bpf/core-attr-6.c 
b/gcc/testsuite/gcc.target/bpf/core-attr-6.c
index 25215b5ae376..d43825ea97c3 100644
--- a/gcc/testsuite/gcc.target/bpf/core-attr-6.c
+++ b/gcc/testsuite/gcc.target/bpf/core-attr-6.c
@@ -11,8 +11,8 @@ struct U {
     int e[4];
     int f;
     int *g;
-  } v;
-} u;
+  } __attribute__((preserve_access_index)) v;
+} __attribute__((preserve_access_index)) u;
 
 struct T {
   int a;
diff --git a/gcc/testsuite/gcc.target/bpf/core-attr-7.c 
b/gcc/testsuite/gcc.target/bpf/core-attr-7.c
new file mode 100644
index 000000000000..342f69e379ba
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-attr-7.c
@@ -0,0 +1,150 @@
+/* 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 -masm=normal" } */
+
+struct other
+{
+  char c;
+  int i;
+};
+
+struct inner_attr1
+{
+  int i1;
+  int i2;
+} __attribute__((preserve_access_index));
+
+struct inner_noattr
+{
+  int i1;
+  int i2;
+};
+
+union A_noattr
+{
+  struct inner_attr1 inner_attr;
+  struct inner_noattr inner_no_attr;
+  struct inner_noattr *inner_p;
+};
+union A_attr
+{
+  struct inner_attr1 inner_attr;
+  struct inner_noattr inner_no_attr;
+  struct inner_noattr *inner_p;
+} __attribute__((preserve_access_index));
+
+
+struct outer_noattr
+{
+  struct other *other;
+  struct inner_attr
+  {
+    int i1;
+    int i2;
+  } __attribute__((preserve_access_index)) inner;
+  struct inner_noattr inner_no_attr;
+  struct inner_attr1 *inner_p;
+  union A_attr a_attr;
+  union A_noattr a_noattr;
+};
+
+struct outer_attr
+{
+  struct other *other;
+  struct inner_attr
+  {
+    int i1;
+    int i2;
+  } __attribute__((preserve_access_index)) inner;
+
+  struct inner_noattr inner_no_attr;
+  struct inner_attr1 *inner_p;
+  union A_attr a_attr;
+  union A_noattr a_noattr;
+} __attribute__((preserve_access_index));
+
+extern void use_value(int);
+
+void
+func (struct outer_noattr *o, struct outer_attr *o_attr)
+{
+  /* 0:1 */
+  o->inner.i2 = 7;
+  /* 0:1 */
+  o->inner_p->i2 = 8;
+  /* nothing */
+  o->inner_no_attr.i2 = 9;
+
+  /* 0:2 */
+  o_attr->inner_no_attr.i2 = 10;
+
+  /* nothing */
+  o->a_noattr.inner_no_attr.i1 = 1;
+  /* 0:1 */
+  o->a_attr.inner_no_attr.i1 = 2;
+  /* 0:0 */
+  o->a_noattr.inner_attr.i1 = 3;
+  /* 0:4:0:0 */
+  o_attr->a_attr.inner_attr.i1 = 4;
+  /* 0:5       0:0 */
+  o_attr->a_noattr.inner_attr.i1 = 5;
+
+  /* This would still force everything as being attributed, although none of
+     the structs has the attribute.  */
+  /* 0:5:2     0:0 */
+  __builtin_preserve_access_index (({ o->a_noattr.inner_p->i1 = 20; }));
+
+  /* 0:2 */
+  struct inner_noattr d = o_attr->inner_no_attr;
+  use_value(d.i2);
+}
+
+
+extern void use(void *);
+
+struct udphdr {
+       int source;
+       int dest;
+       int len;
+       int check;
+} __attribute__((preserve_access_index));
+
+union l4hdr {
+       struct udphdr udp;
+       int c;
+};
+
+struct v4hdr {
+       int a;
+       union l4hdr l4hdr;
+       int b;
+};
+
+void gimple_loop_error(void)
+{
+  struct v4hdr h_outer;
+  h_outer.l4hdr.udp.source = 1024;
+  use(&h_outer.l4hdr.udp.source);
+}
+
+/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t 
\]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t 
\]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t 
\]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:5.0\"\[\t 
\]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:4:0:0.0\"\[\t 
\]+\[^\n\]*btf_aux_string" 1 } } */
+/* { dg-final { scan-assembler-times "ascii \"0:5:2.0\"\[\t 
\]+\[^\n\]*btf_aux_string" 1 } } */
+
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:0\"\\)" 4 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:1\"\\)" 3 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:2\"\\)" 2 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:5\"\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:4:0:0\"\\)" 1 } } 
*/
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:5:2\"\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct inner_attr\\)" 1 } 
} */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct inner_attr1\\)" 3 } 
} */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct outer_attr\\)" 4 } 
} */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(union A_attr\\)" 1 } } */
+
+
diff --git a/gcc/testsuite/gcc.target/bpf/core-attr-calls.c 
b/gcc/testsuite/gcc.target/bpf/core-attr-calls.c
index 87290c5c2116..27b08af1bb79 100644
--- a/gcc/testsuite/gcc.target/bpf/core-attr-calls.c
+++ b/gcc/testsuite/gcc.target/bpf/core-attr-calls.c
@@ -37,13 +37,13 @@ func (struct T *t, int i)
   /* 0:3  */
   get_other_u(t->ptr_u)->c = 43;
 
-  /* 0:2:1  */
+  /* 0:2  */
   get_other_v(&t->u.v)->d = 44;
 }
 
 /* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:3\"\\)" 2 } } */
 /* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:0\"\\)" 1 } } */
-/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:2:1\"\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:2\"\\)" 1 } } */
 /* { dg-final { scan-assembler-times "bpfcr_type \\(struct T\\)" 3 } } */
 /* { dg-final { scan-assembler-times "bpfcr_type \\(struct U\\)" 1 } } */
 
-- 
2.47.3


Reply via email to