Pushed !

Thanks,
Cupertino

On 23-02-2026 10:54 PM, David Faust wrote:


On 2/23/26 14:24, Cupertino Miranda wrote:
Hi everyone,

This is v5.
Thank you David for your prompt reviews and nice catches.
Apologies for any missing obvious mistakes.

Looking forward to your review.

Thanks to you for continuing to work on this.

There is a typo in the new warning message, noted inline below.

But other than that, this version LGTM,
OK with that warning message fixed.

Thanks!


Cheers,
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.
        doc/invoke.texi: Add documentation for Wbpf-core.

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/doc/invoke.texi                           |   8 +-
  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 +-
  9 files changed, 378 insertions(+), 108 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..e57249a45767 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.  We need to confirm if the attribute "packed"
+   should disable any CO-RE access in any of its children fields.  */
+  if (lookup_attribute ("packed", TYPE_ATTRIBUTES (TREE_TYPE (*tp))))
+  {
+    warning_at (EXPR_LOC_OR_LOC (*tp, UNKNOWN_LOCATION), OPT_Wbpf_core,
+       "Access through packed struct may not be possito to patch via CO-RE");

Typo here, I guess this is meant to say "...possible to patch..."


+    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 dereference
+            the type within the pointer to guarantee it is 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/doc/invoke.texi b/gcc/doc/invoke.texi
index df4331fbad07..cfebd208ab0c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1074,7 +1074,8 @@ Objective-C and Objective-C++ Dialects}.
  @gccoptlist{-mbig-endian -mlittle-endian
  -mframe-limit=@var{bytes} -mxbpf -mco-re -mno-co-re -mjmpext
  -mjmp32 -malu32 -mv3-atomics -mbswap -msdiv -msmov -mcpu=@var{version}
--masm=@var{dialect} -minline-memops-threshold=@var{bytes}}
+-masm=@var{dialect} -minline-memops-threshold=@var{bytes}
+-Wbpf-core}
@emph{FR30 Options} (@ref{FR30 Options})
  @gccoptlist{-msmall-model  -mno-lsim}
@@ -27543,6 +27544,11 @@ a library call instead of being expanded inline, but 
since BPF doesn't
  allow libcalls, exceeding this threshold results in a compile-time
  error.  The default is @samp{1024} bytes.
+@opindex Wco-re
+@item -Wco-re
+Enable warnings for scenarios where -mco-re is enabled, but it may not be
+possible to generate CO-RE compatible code.
+
  @end table
@node FR30 Options
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 } } */



Reply via email to