Hi David,
Thanks for the comments. I have just realized that I introduced several errors in the patch by mistake right before I sent it. Like the missing "*". Apologies, I will revise it and send a new patch in a moment. Thanks, Cupertino David Faust writes: > On 10/25/23 11:51, Cupertino Miranda wrote: >> Hi everyone, >> >> This patch contains some more recent improvements to BPF CO-RE builtins. >> Please find further details of the changes on the patch header. >> >> Looking forward for your review and comments. >> >> Best regards, >> Cupertino Miranda > > Hi Cupertino, > > Thanks for continuing to work on these BPF builtins. > > Comments inline below. > >> >> >> bpf_builtins_upstream.patch >> >> commit 6054209c0a8af9c3e6363550bf2ba4f4f2172eba >> Author: Cupertino Miranda <cupertino.mira...@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. > > It may only be due to how the patch is formatted in the attachment > (everything above the first diff seems to be indented?), but each entry > here should start with a tab rather than spaces. > > Please double-check with contrib/gcc-changelog/git_check_commit.py, it > will complain if the indentation is wrong. > >> * 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>): Changed > > Looks like an entry along the lines of "Renamed to..." would be > more fitting. > >> * config/bpf/core-builtins.cc (cr_builtins, >> is_attr_preserve_access, >> core_field_info, bpf_core_get_index, compute_field_expr, >> process_field_expr, pack_type, make_core_relo, >> bpf_handle_plugin_finish_type, core_buintin_helpers, >> construct_builtin_core_reloc, >> bpf_resolve_overloaded_core_builtin, >> bpf_add_core_reloc): Changed. > > "Changed." as a ChangeLog entry is not useful. Please be at least a > little bit more descriptive here so someone only reading the ChangeLog > has an idea of what they're looking at. > > Also, I'm not sure this is a proper way to note multiple functions > with the same entry, usually this is done with a new pair of () for > each new line, like: > > * config/bpf/core-builtins.cc (cr_builtins, is_attr_preserve_access) > (core_field_info, bpf_core_get_index): Changed to do blah. > > contrib/gcc-changelog/git_check_commit.py might complain about this; > please check. > >> (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. > > Same thing about the multi-line entry within a single () pair. > >> (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..249c58e22067 >> --- /dev/null >> +++ b/gcc/config/bpf/bpf-passes.def >> @@ -0,0 +1,20 @@ >> +/* Declaration of target-specific passes for eBPF. >> + Copyright (C) 2021-2023 Free Software Foundation, Inc. > > This file is new, so just 2023 no? > >> + >> + 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..a340cd742d54 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 true; >> + >> + return false; >> +} >> + >> +#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..7a9488c27124 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 refered through the document >> as > > typo: referred > >> + 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 convertion is done in an independent gimple pass very > > typo: conversion > >> + 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_acces_index rely on a tree walker that > > typo: preserve_access_index > >> + 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,10 +318,16 @@ 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))); >> + > > Extra blank newline. > >> } >> >> if (TREE_CODE (t) == COMPONENT_REF) >> @@ -336,10 +341,12 @@ is_attr_preserve_access (tree t) >> if (TREE_CODE (op) == COMPONENT_REF) >> return is_attr_preserve_access (op); >> >> + > > Likewise, spurious newline. > >> 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 +355,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 +390,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 +527,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 +567,105 @@ 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 argumenta and specifies if the function >> should > > typo: argumenta -> arguments > >> + consider as valid expressions in which NODE entry is a cast expression >> (or >> + tree code nop_expr). >> + */ > > nit: this */ should be on the preceding line. > >> >> 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 +675,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 +722,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 +737,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 +765,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 +784,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 +798,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 +824,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 +858,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 +873,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 +896,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 +916,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 +947,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) >> { >> @@ -976,7 +1010,7 @@ make_core_relo (struct cr_final *data, rtx_code_label >> *label) >> /* Determine what output section this relocation will apply to. >> If this function is associated with a section, use that. Otherwise, >> fall back on '.text'. */ >> - const char * section_name; >> + const char section_name; > > Why this change from char* to char? > > This change doesn't make sense given the expressions assigned to > the variable. In fact, this does not compile. > >> if (current_function_decl && DECL_SECTION_NAME (current_function_decl)) >> section_name = DECL_SECTION_NAME (current_function_decl); >> else >> @@ -1075,30 +1109,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 +1118,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 +1130,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 +1148,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 +1182,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 +1219,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 +1233,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]; >> + >> + 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); >> >> - 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)); >> + *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 +1488,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 +1534,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) >> @@ -1297,7 +1544,7 @@ bpf_add_core_reloc (rtx *operands, const char *templ) >> builtin_helpers helper; >> helper = core_builtin_helpers[data->orig_builtin_code]; >> >> - rtx_code_label * tmp_label = gen_label_rtx (); >> + rtx_code_label tmp_label = gen_label_rtx (); > > Hm, again why drop the '*'? This doesn't make sense. > Maybe your editor did something weird? > > extern rtx_code_label *gen_label_rtx (void); > >> output_asm_label (tmp_label); >> assemble_name (asm_out_file, ":\n"); >> >> @@ -1306,67 +1553,196 @@ 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; >> + >> + if (!wi->is_lhs) >> + core_mark_as_access_index (gimple_get_lhs (wi->stmt)); >> + } >> + 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 } } */ >>