> Hi Cupertino,
>
> On 2/19/26 12:38, Cupertino Miranda wrote:
>> 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.
>
> Overall this version is looking quite nice.
> Just a couple of notes inline below.
> Thanks!
>
>>
>> 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
>
> Very good.
>
>>
>> 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.
>
> I like this new warning option.
>
> But it needs a corresponding entry and documentation in doc/invoke.texi
> under the BPF Options.
And please then remember to regenerate bpf.opt.urls (which I always
forget)... :)
>
>
>> 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. */
>
> What behavior? Please elaborate a little in the comment.
>
>> + 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");
>
> This warning message as written doesn't make much sense to me.
>
> From what I understand (also based on discussions in IRC), the problem
> is that if we access a p-a-i struct member of a struct which is packed
> like in:
>
> struct A {
> int x;
> } __attribute__((preserve_access_index))
>
> struct B {
> struct A a;
> } __attribute__((packed))
>
> int foo (struct B *b)
> {
> return b->a.x;
> }
>
> ...Then it may not be possible to always patch the instructions which
> access 'a.x' depending on how the structs change on a different kernel
> version. Right?
>
> I suggest rewording the warning message to something like
>
> "Access [to member x] through packed struct may not be possible
> to patch via CO-RE"
>
> (bonus points for stuff in [] or precise location info to point to it)
>
>
>
>> + 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;
>
> very minor nit but I noticed it so: dot, space, space, star slash
> for the two comments above.
>
>> +}
>>
>> 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. */
>
> nit but the block comment styling should not prepend * on every line.
>
>> + 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 } } */
>>