Pushed to upstream master.
Thanks ! Jose E. Marchesi writes: > Ok. > Thanks! > >> From fda9603ded735205b6e20fc5b65a04f8d15685e6 Mon Sep 17 00:00:00 2001 >> From: Cupertino Miranda <cupertino.mira...@oracle.com> >> Date: Thu, 6 Apr 2023 15:22:48 +0100 >> Subject: [PATCH v2 1/2] bpf: Implementation of BPF CO-RE builtins >> >> This patch updates the support for the BPF CO-RE builtins >> __builtin_preserve_access_index and __builtin_preserve_field_info, >> and adds support for the CO-RE builtins __builtin_btf_type_id, >> __builtin_preserve_type_info and __builtin_preserve_enum_value. >> >> These CO-RE relocations are now converted to __builtin_core_reloc which >> abstracts all of the original builtins in a polymorphic relocation >> specific builtin. >> >> The builtin processing is now split in 2 stages, the first (pack) is >> executed right after the front-end and the second (process) right before >> the asm output. >> >> In expand pass the __builtin_core_reloc is converted to a >> unspec:UNSPEC_CORE_RELOC rtx entry. >> >> The data required to process the builtin is now collected in the packing >> stage (after front-end), not allowing the compiler to optimize any of >> the relevant information required to compose the relocation when >> necessary. >> At expansion, that information is recovered and CTF/BTF is queried to >> construct the information that will be used in the relocation. >> At this point the relocation is added to specific section and the >> builtin is expanded to the expected default value for the builtin. >> >> In order to process __builtin_preserve_enum_value, it was necessary to >> hook the front-end to collect the original enum value reference. >> This is needed since the parser folds all the enum values to its >> integer_cst representation. >> >> More details can be found within the core-builtins.cc. >> >> Regtested in host x86_64-linux-gnu and target bpf-unknown-none. >> --- >> gcc/config.gcc | 4 +- >> gcc/config/bpf/bpf-passes.def | 20 - >> gcc/config/bpf/bpf-protos.h | 4 +- >> gcc/config/bpf/bpf.cc | 806 ++---------------- >> gcc/config/bpf/bpf.md | 17 + >> gcc/config/bpf/core-builtins.cc | 1394 +++++++++++++++++++++++++++++++ >> gcc/config/bpf/core-builtins.h | 35 + >> gcc/config/bpf/coreout.cc | 50 +- >> gcc/config/bpf/coreout.h | 13 +- >> gcc/config/bpf/t-bpf | 6 +- >> gcc/doc/extend.texi | 51 ++ >> 11 files changed, 1595 insertions(+), 805 deletions(-) >> delete mode 100644 gcc/config/bpf/bpf-passes.def >> create mode 100644 gcc/config/bpf/core-builtins.cc >> create mode 100644 gcc/config/bpf/core-builtins.h >> >> diff --git a/gcc/config.gcc b/gcc/config.gcc >> index eba69a463be0..c521669e78b1 100644 >> --- a/gcc/config.gcc >> +++ b/gcc/config.gcc >> @@ -1597,8 +1597,8 @@ bpf-*-*) >> use_collect2=no >> extra_headers="bpf-helpers.h" >> use_gcc_stdint=provide >> - extra_objs="coreout.o" >> - target_gtfiles="$target_gtfiles \$(srcdir)/config/bpf/coreout.cc" >> + extra_objs="coreout.o core-builtins.o" >> + target_gtfiles="$target_gtfiles \$(srcdir)/config/bpf/coreout.cc >> \$(srcdir)/config/bpf/core-builtins.cc" >> ;; >> cris-*-elf | cris-*-none) >> tm_file="elfos.h newlib-stdint.h ${tm_file}" >> diff --git a/gcc/config/bpf/bpf-passes.def b/gcc/config/bpf/bpf-passes.def >> deleted file mode 100644 >> index deeaee988a01..000000000000 >> --- a/gcc/config/bpf/bpf-passes.def >> +++ /dev/null >> @@ -1,20 +0,0 @@ >> -/* Declaration of target-specific passes for eBPF. >> - Copyright (C) 2021-2023 Free Software Foundation, Inc. >> - >> - This file is part of GCC. >> - >> - GCC is free software; you can redistribute it and/or modify it >> - under the terms of the GNU General Public License as published by >> - the Free Software Foundation; either version 3, or (at your option) >> - any later version. >> - >> - GCC is distributed in the hope that it will be useful, but >> - WITHOUT ANY WARRANTY; without even the implied warranty of >> - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> - General Public License for more details. >> - >> - You should have received a copy of the GNU General Public License >> - along with GCC; see the file COPYING3. If not see >> - <http://www.gnu.org/licenses/>. */ >> - >> -INSERT_PASS_AFTER (pass_df_initialize_opt, 1, pass_bpf_core_attr); >> diff --git a/gcc/config/bpf/bpf-protos.h b/gcc/config/bpf/bpf-protos.h >> index b484310e8cbf..fbe0d8a0213f 100644 >> --- a/gcc/config/bpf/bpf-protos.h >> +++ b/gcc/config/bpf/bpf-protos.h >> @@ -30,7 +30,7 @@ extern void bpf_print_operand_address (FILE *, rtx); >> extern void bpf_expand_prologue (void); >> extern void bpf_expand_epilogue (void); >> extern void bpf_expand_cbranch (machine_mode, rtx *); >> - >> -rtl_opt_pass * make_pass_bpf_core_attr (gcc::context *); >> +const char *bpf_add_core_reloc (rtx *operands, const char *templ); >> +void bpf_replace_core_move_operands (rtx *operands); >> >> #endif /* ! GCC_BPF_PROTOS_H */ >> diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc >> index 57817cdf2f86..4873475e73bd 100644 >> --- a/gcc/config/bpf/bpf.cc >> +++ b/gcc/config/bpf/bpf.cc >> @@ -69,10 +69,7 @@ along with GCC; see the file COPYING3. If not see >> #include "gimplify.h" >> #include "gimplify-me.h" >> >> -#include "ctfc.h" >> -#include "btf.h" >> - >> -#include "coreout.h" >> +#include "core-builtins.h" >> >> /* Per-function machine data. */ >> struct GTY(()) machine_function >> @@ -174,22 +171,7 @@ static const struct attribute_spec >> bpf_attribute_table[] = >> one. */ >> #define BPF_BUILTIN_MAX_ARGS 5 >> >> -enum bpf_builtins >> -{ >> - BPF_BUILTIN_UNUSED = 0, >> - /* Built-ins for non-generic loads and stores. */ >> - BPF_BUILTIN_LOAD_BYTE, >> - BPF_BUILTIN_LOAD_HALF, >> - BPF_BUILTIN_LOAD_WORD, >> - >> - /* Compile Once - Run Everywhere (CO-RE) support. */ >> - BPF_BUILTIN_PRESERVE_ACCESS_INDEX, >> - BPF_BUILTIN_PRESERVE_FIELD_INFO, >> - >> - BPF_BUILTIN_MAX, >> -}; >> - >> -static GTY (()) tree bpf_builtins[(int) BPF_BUILTIN_MAX]; >> +GTY (()) tree bpf_builtins[(int) BPF_BUILTIN_MAX]; >> >> void bpf_register_coreattr_pass (void); >> >> @@ -855,6 +837,7 @@ bpf_output_call (rtx target) >> Additionally, the code 'w' denotes that the register should be printed >> as wN instead of rN, where N is the register number, but only when the >> value stored in the operand OP is 32-bit wide. */ >> + >> static void >> bpf_print_register (FILE *file, rtx op, int code) >> { >> @@ -981,13 +964,14 @@ bpf_print_operand_address (FILE *file, rtx addr) >> /* Add a BPF builtin function with NAME, CODE and TYPE. Return >> the function decl or NULL_TREE if the builtin was not added. */ >> >> -static tree >> +static inline tree >> def_builtin (const char *name, enum bpf_builtins code, tree type) >> { >> tree t >> - = add_builtin_function (name, type, code, BUILT_IN_MD, NULL, NULL_TREE); >> + = add_builtin_function (name, type, code, BUILT_IN_MD, NULL, NULL); >> >> bpf_builtins[code] = t; >> + >> return t; >> } >> >> @@ -1006,214 +990,40 @@ bpf_init_builtins (void) >> build_function_type_list (ullt, ullt, 0)); >> def_builtin ("__builtin_bpf_load_word", BPF_BUILTIN_LOAD_WORD, >> build_function_type_list (ullt, ullt, 0)); >> + >> def_builtin ("__builtin_preserve_access_index", >> BPF_BUILTIN_PRESERVE_ACCESS_INDEX, >> build_function_type_list (ptr_type_node, ptr_type_node, 0)); >> def_builtin ("__builtin_preserve_field_info", >> BPF_BUILTIN_PRESERVE_FIELD_INFO, >> - build_function_type_list (unsigned_type_node, ptr_type_node, >> unsigned_type_node, 0)); >> + build_function_type_list (unsigned_type_node, ptr_type_node, >> + unsigned_type_node, 0)); >> + def_builtin ("__builtin_btf_type_id", >> + BPF_BUILTIN_BTF_TYPE_ID, >> + build_function_type_list (integer_type_node, ptr_type_node, >> + integer_type_node, 0)); >> + def_builtin ("__builtin_preserve_type_info", >> + BPF_BUILTIN_PRESERVE_TYPE_INFO, >> + build_function_type_list (integer_type_node, ptr_type_node, >> + integer_type_node, 0)); >> + def_builtin ("__builtin_preserve_enum_value", >> + BPF_BUILTIN_PRESERVE_ENUM_VALUE, >> + build_function_type_list (integer_type_node, ptr_type_node, >> + integer_type_node, integer_type_node, >> + 0)); >> + >> + def_builtin ("__builtin_core_reloc", >> + BPF_BUILTIN_CORE_RELOC, >> + build_function_type_list (integer_type_node,integer_type_node, >> + 0)); >> + DECL_PURE_P (bpf_builtins[BPF_BUILTIN_CORE_RELOC]) = 1; >> + >> + bpf_init_core_builtins (); >> } >> >> #undef TARGET_INIT_BUILTINS >> #define TARGET_INIT_BUILTINS bpf_init_builtins >> >> -static tree bpf_core_compute (tree, vec<unsigned int> *); >> -static int bpf_core_get_index (const tree); >> -static bool is_attr_preserve_access (tree); >> - >> -/* BPF Compile Once - Run Everywhere (CO-RE) support. Construct a CO-RE >> - relocation record for EXPR of kind KIND to be emitted in the .BTF.ext >> - section. Does nothing if we are not targetting BPF CO-RE, or if the >> - constructed relocation would be a no-op. */ >> - >> -static void >> -maybe_make_core_relo (tree expr, enum btf_core_reloc_kind kind) >> -{ >> - /* If we are not targetting BPF CO-RE, do not make a relocation. We >> - might not be generating any debug info at all. */ >> - if (!TARGET_BPF_CORE) >> - return; >> - >> - auto_vec<unsigned int, 16> accessors; >> - tree container = bpf_core_compute (expr, &accessors); >> - >> - /* Any valid use of the builtin must have at least one access. Otherwise, >> - there is nothing to record and nothing to do. This is primarily a >> - guard against optimizations leading to unexpected expressions in the >> - argument of the builtin. For example, if the builtin is used to read >> - a field of a structure which can be statically determined to hold a >> - constant value, the argument to the builtin will be optimized to that >> - constant. This is OK, and means the builtin call is superfluous. >> - e.g. >> - struct S foo; >> - foo.a = 5; >> - int x = __preserve_access_index (foo.a); >> - ... do stuff with x >> - 'foo.a' in the builtin argument will be optimized to '5' with -01+. >> - This sequence does not warrant recording a CO-RE relocation. */ >> - >> - if (accessors.length () < 1) >> - return; >> - accessors.reverse (); >> - >> - rtx_code_label *label = gen_label_rtx (); >> - LABEL_PRESERVE_P (label) = 1; >> - emit_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; >> - if (current_function_decl && DECL_SECTION_NAME (current_function_decl)) >> - section_name = DECL_SECTION_NAME (current_function_decl); >> - else >> - section_name = ".text"; >> - >> - /* Add the CO-RE relocation information to the BTF container. */ >> - bpf_core_reloc_add (TREE_TYPE (container), section_name, &accessors, >> label, >> - kind); >> -} >> - >> -/* Expand a call to __builtin_preserve_field_info by evaluating the >> requested >> - information about SRC according to KIND, and return a tree holding >> - the result. */ >> - >> -static tree >> -bpf_core_field_info (tree src, enum btf_core_reloc_kind kind) >> -{ >> - unsigned int result; >> - poly_int64 bitsize, bitpos; >> - tree var_off = NULL_TREE; >> - machine_mode mode; >> - int unsignedp, reversep, volatilep; >> - location_t loc = EXPR_LOCATION (src); >> - >> - get_inner_reference (src, &bitsize, &bitpos, &var_off, &mode, &unsignedp, >> - &reversep, &volatilep); >> - >> - /* Note: Use DECL_BIT_FIELD_TYPE rather than DECL_BIT_FIELD here, because >> it >> - remembers whether the field in question was originally declared as a >> - bitfield, regardless of how it has been optimized. */ >> - bool bitfieldp = (TREE_CODE (src) == COMPONENT_REF >> - && DECL_BIT_FIELD_TYPE (TREE_OPERAND (src, 1))); >> - >> - unsigned int align = TYPE_ALIGN (TREE_TYPE (src)); >> - if (TREE_CODE (src) == COMPONENT_REF) >> - { >> - tree field = TREE_OPERAND (src, 1); >> - if (DECL_BIT_FIELD_TYPE (field)) >> - align = TYPE_ALIGN (DECL_BIT_FIELD_TYPE (field)); >> - else >> - align = TYPE_ALIGN (TREE_TYPE (field)); >> - } >> - >> - unsigned int start_bitpos = bitpos & ~(align - 1); >> - unsigned int end_bitpos = start_bitpos + align; >> - >> - switch (kind) >> - { >> - case BPF_RELO_FIELD_BYTE_OFFSET: >> - { >> - if (var_off != NULL_TREE) >> - { >> - error_at (loc, "unsupported variable field offset"); >> - return error_mark_node; >> - } >> - >> - if (bitfieldp) >> - result = start_bitpos / 8; >> - else >> - result = bitpos / 8; >> - } >> - break; >> - >> - case BPF_RELO_FIELD_BYTE_SIZE: >> - { >> - if (mode == BLKmode && bitsize == -1) >> - { >> - error_at (loc, "unsupported variable size field access"); >> - return error_mark_node; >> - } >> - >> - if (bitfieldp) >> - { >> - /* To match LLVM behavior, byte size of bitfields is recorded as >> - the full size of the base type. A 3-bit bitfield of type int is >> - therefore recorded as having a byte size of 4 bytes. */ >> - result = end_bitpos - start_bitpos; >> - if (result & (result - 1)) >> - { >> - error_at (loc, "unsupported field expression"); >> - return error_mark_node; >> - } >> - result = result / 8; >> - } >> - else >> - result = bitsize / 8; >> - } >> - break; >> - >> - case BPF_RELO_FIELD_EXISTS: >> - /* The field always exists at compile time. */ >> - result = 1; >> - break; >> - >> - case BPF_RELO_FIELD_SIGNED: >> - result = !unsignedp; >> - break; >> - >> - case BPF_RELO_FIELD_LSHIFT_U64: >> - case BPF_RELO_FIELD_RSHIFT_U64: >> - { >> - if (mode == BLKmode && bitsize == -1) >> - { >> - error_at (loc, "unsupported variable size field access"); >> - return error_mark_node; >> - } >> - if (var_off != NULL_TREE) >> - { >> - error_at (loc, "unsupported variable field offset"); >> - return error_mark_node; >> - } >> - >> - if (!bitfieldp) >> - { >> - if (bitsize > 64) >> - { >> - error_at (loc, "field size too large"); >> - return error_mark_node; >> - } >> - result = 64 - bitsize; >> - break; >> - } >> - >> - if (end_bitpos - start_bitpos > 64) >> - { >> - error_at (loc, "field size too large"); >> - return error_mark_node; >> - } >> - >> - if (kind == BPF_RELO_FIELD_LSHIFT_U64) >> - { >> - if (TARGET_BIG_ENDIAN) >> - result = bitpos + 64 - start_bitpos - align; >> - else >> - result = start_bitpos + 64 - bitpos - bitsize; >> - } >> - else /* RSHIFT_U64 */ >> - result = 64 - bitsize; >> - } >> - break; >> - >> - default: >> - error ("invalid second argument to built-in function"); >> - return error_mark_node; >> - break; >> - } >> - >> - return build_int_cst (unsigned_type_node, result); >> -} >> - >> /* Expand a call to a BPF-specific built-in function that was set up >> with bpf_init_builtins. */ >> >> @@ -1264,73 +1074,34 @@ bpf_expand_builtin (tree exp, rtx target >> ATTRIBUTE_UNUSED, >> /* The result of the load is in R0. */ >> return gen_rtx_REG (ops[0].mode, BPF_R0); >> } >> - >> - else if (code == -BPF_BUILTIN_PRESERVE_ACCESS_INDEX) >> - { >> - /* A resolved overloaded __builtin_preserve_access_index. */ >> - tree arg = CALL_EXPR_ARG (exp, 0); >> - >> - if (arg == NULL_TREE) >> - return NULL_RTX; >> - >> - if (TREE_CODE (arg) == SSA_NAME) >> - { >> - gimple *def_stmt = SSA_NAME_DEF_STMT (arg); >> - >> - if (is_gimple_assign (def_stmt)) >> - arg = gimple_assign_rhs1 (def_stmt); >> - else >> - return expand_normal (arg); >> - } >> - >> - /* Avoid double-recording information if the 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)) >> - maybe_make_core_relo (arg, BPF_RELO_FIELD_BYTE_OFFSET); >> - >> - return expand_normal (arg); >> - } >> - >> - else if (code == -BPF_BUILTIN_PRESERVE_FIELD_INFO) >> + else >> { >> - /* A resolved overloaded __builtin_preserve_field_info. */ >> - tree src = CALL_EXPR_ARG (exp, 0); >> - tree kind_tree = CALL_EXPR_ARG (exp, 1); >> - unsigned HOST_WIDE_INT kind_val = 0; >> - if (tree_fits_uhwi_p (kind_tree)) >> - kind_val = tree_to_uhwi (kind_tree); >> - else >> - { >> - error ("invalid argument to built-in function"); >> - return expand_normal (error_mark_node); >> - } >> - >> - enum btf_core_reloc_kind kind = (enum btf_core_reloc_kind) kind_val; >> - >> - if (TREE_CODE (src) == SSA_NAME) >> - { >> - gimple *def_stmt = SSA_NAME_DEF_STMT (src); >> - if (is_gimple_assign (def_stmt)) >> - src = gimple_assign_rhs1 (def_stmt); >> - } >> - if (TREE_CODE (src) == ADDR_EXPR) >> - src = TREE_OPERAND (src, 0); >> - >> - tree result = bpf_core_field_info (src, kind); >> - >> - if (result != error_mark_node) >> - maybe_make_core_relo (src, kind); >> - >> - return expand_normal (result); >> + rtx ret = bpf_expand_core_builtin (exp, (enum bpf_builtins) code); >> + if (ret != NULL_RTX) >> + return ret; >> } >> >> + error ("invalid built-in function at expansion"); >> gcc_unreachable (); >> } >> >> #undef TARGET_EXPAND_BUILTIN >> #define TARGET_EXPAND_BUILTIN bpf_expand_builtin >> >> +static tree >> +bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist) >> +{ >> + int code = DECL_MD_FUNCTION_CODE (fndecl); >> + if (code > BPF_CORE_BUILTINS_MARKER) >> + return bpf_resolve_overloaded_core_builtin (loc, fndecl, arglist); >> + else >> + return NULL_TREE; >> +} >> + >> +#undef TARGET_RESOLVE_OVERLOADED_BUILTIN >> +#define TARGET_RESOLVE_OVERLOADED_BUILTIN bpf_resolve_overloaded_builtin >> + >> + >> /* Initialize target-specific function library calls. This is mainly >> used to call library-provided soft-fp operations, since eBPF >> doesn't support floating-point in "hardware". */ >> @@ -1378,214 +1149,6 @@ bpf_debug_unwind_info () >> #undef TARGET_ASM_ALIGNED_DI_OP >> #define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t" >> >> - >> -/* BPF Compile Once - Run Everywhere (CO-RE) support routines. >> - >> - BPF CO-RE 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. >> - >> - The pass pass_bpf_core_attr, below, implements support for >> - this attribute. */ >> - >> -/* Traverse the subtree under NODE, which is expected to be some form of >> - aggregate access the CO-RE machinery cares about (like a read of a >> member of >> - a struct or union), collecting access indices for the components and >> storing >> - them in the vector referenced by ACCESSORS. >> - >> - Return the ultimate (top-level) container of the aggregate access. In >> general, >> - this will be a VAR_DECL or some kind of REF. >> - >> - Note that the accessors are computed *in reverse order* of how the BPF >> - CO-RE machinery defines them. The vector needs to be reversed (or simply >> - output in reverse order) for the .BTF.ext relocation information. */ >> - >> -static tree >> -bpf_core_compute (tree node, vec<unsigned int> *accessors) >> -{ >> - >> - if (TREE_CODE (node) == ADDR_EXPR) >> - node = TREE_OPERAND (node, 0); >> - >> - else if (INDIRECT_REF_P (node) >> - || TREE_CODE (node) == POINTER_PLUS_EXPR) >> - { >> - accessors->safe_push (0); >> - return TREE_OPERAND (node, 0); >> - } >> - >> - while (1) >> - { >> - switch (TREE_CODE (node)) >> - { >> - case COMPONENT_REF: >> - accessors->safe_push (bpf_core_get_index (TREE_OPERAND (node, 1))); >> - break; >> - >> - case ARRAY_REF: >> - case ARRAY_RANGE_REF: >> - accessors->safe_push (bpf_core_get_index (node)); >> - break; >> - >> - case MEM_REF: >> - accessors->safe_push (bpf_core_get_index (node)); >> - if (TREE_CODE (TREE_OPERAND (node, 0)) == ADDR_EXPR) >> - node = TREE_OPERAND (TREE_OPERAND (node, 0), 0); >> - goto done; >> - >> - default: >> - goto done; >> - } >> - node = TREE_OPERAND (node, 0); >> - } >> - done: >> - return node; >> - >> -} >> - >> -/* Compute the index of the NODE in its immediate container. >> - 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) >> -{ >> - enum tree_code code = TREE_CODE (node); >> - >> - if (code == FIELD_DECL) >> - { >> - /* Lookup the index from the BTF information. Some struct/union >> members >> - may not be emitted in BTF; only the BTF container has enough >> - information to compute the correct index. */ >> - int idx = bpf_core_get_sou_member_index (ctf_get_tu_ctfc (), node); >> - if (idx >= 0) >> - return idx; >> - } >> - >> - else if (code == ARRAY_REF || code == ARRAY_RANGE_REF || code == MEM_REF) >> - { >> - /* For array accesses, the index is operand 1. */ >> - tree index = TREE_OPERAND (node, 1); >> - >> - /* If the indexing operand is a constant, extracting is trivial. */ >> - if (TREE_CODE (index) == INTEGER_CST && tree_fits_shwi_p (index)) >> - return tree_to_shwi (index); >> - } >> - >> - return -1; >> -} >> - >> -/* Synthesize a new builtin function declaration with signature TYPE. >> - Used by bpf_resolve_overloaded_builtin to resolve calls to >> - __builtin_preserve_access_index. */ >> - >> -static tree >> -bpf_core_newdecl (tree type, enum bpf_builtins which) >> -{ >> - tree rettype; >> - char name[80]; >> - static unsigned long pai_count = 0; >> - static unsigned long pfi_count = 0; >> - >> - switch (which) >> - { >> - case BPF_BUILTIN_PRESERVE_ACCESS_INDEX: >> - { >> - rettype = build_function_type_list (type, type, NULL); >> - int len = snprintf (name, sizeof (name), "%s", "__builtin_pai_"); >> - len = snprintf (name + len, sizeof (name) - len, "%lu", pai_count++); >> - } >> - break; >> - >> - case BPF_BUILTIN_PRESERVE_FIELD_INFO: >> - { >> - rettype = build_function_type_list (unsigned_type_node, type, >> - unsigned_type_node, NULL); >> - int len = snprintf (name, sizeof (name), "%s", "__builtin_pfi_"); >> - len = snprintf (name + len, sizeof (name) - len, "%lu", pfi_count++); >> - } >> - break; >> - >> - default: >> - gcc_unreachable (); >> - } >> - >> - return add_builtin_function_ext_scope (name, rettype, -which, >> - BUILT_IN_MD, NULL, NULL_TREE); >> -} >> - >> -/* Return whether EXPR could access some aggregate data structure that >> - BPF CO-RE support needs to know about. */ >> - >> -static bool >> -bpf_core_is_maybe_aggregate_access (tree expr) >> -{ >> - switch (TREE_CODE (expr)) >> - { >> - case COMPONENT_REF: >> - case BIT_FIELD_REF: >> - case ARRAY_REF: >> - case ARRAY_RANGE_REF: >> - return true; >> - case ADDR_EXPR: >> - case NOP_EXPR: >> - return bpf_core_is_maybe_aggregate_access (TREE_OPERAND (expr, 0)); >> - default: >> - return false; >> - } >> -} >> - >> -struct core_walk_data { >> - location_t loc; >> - enum bpf_builtins which; >> - tree arg; >> -}; >> - >> -/* Callback function used with walk_tree from >> bpf_resolve_overloaded_builtin. */ >> - >> -static tree >> -bpf_core_walk (tree *tp, int *walk_subtrees, void *data) >> -{ >> - struct core_walk_data *dat = (struct core_walk_data *) data; >> - >> - /* If this is a type, don't do anything. */ >> - if (TYPE_P (*tp)) >> - { >> - *walk_subtrees = 0; >> - return NULL_TREE; >> - } >> - >> - /* Build a new function call to a type-resolved temporary builtin for the >> - desired operation, and pass along args as necessary. */ >> - tree newdecl = bpf_core_newdecl (TREE_TYPE (*tp), dat->which); >> - >> - if (dat->which == BPF_BUILTIN_PRESERVE_ACCESS_INDEX) >> - { >> - if (bpf_core_is_maybe_aggregate_access (*tp)) >> - { >> - *tp = build_call_expr_loc (dat->loc, newdecl, 1, *tp); >> - *walk_subtrees = 0; >> - } >> - } >> - else >> - { >> - *tp = build_call_expr_loc (dat->loc, newdecl, 2, *tp, dat->arg); >> - *walk_subtrees = 0; >> - } >> - >> - return NULL_TREE; >> -} >> - >> /* Implement target hook small_register_classes_for_mode_p. */ >> >> static bool >> @@ -1603,277 +1166,6 @@ bpf_small_register_classes_for_mode_p (machine_mode >> mode) >> #define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P \ >> bpf_small_register_classes_for_mode_p >> >> -/* Return whether EXPR is a valid first argument for a call to >> - __builtin_preserve_field_info. */ >> - >> -static bool >> -bpf_is_valid_preserve_field_info_arg (tree expr) >> -{ >> - switch (TREE_CODE (expr)) >> - { >> - case COMPONENT_REF: >> - case BIT_FIELD_REF: >> - case ARRAY_REF: >> - case ARRAY_RANGE_REF: >> - return true; >> - case NOP_EXPR: >> - return bpf_is_valid_preserve_field_info_arg (TREE_OPERAND (expr, 0)); >> - case ADDR_EXPR: >> - /* Do not accept ADDR_EXPRs like &foo.bar, but do accept accesses like >> - foo.baz where baz is an array. */ >> - return (TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == ARRAY_TYPE); >> - default: >> - return false; >> - } >> -} >> - >> -/* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN (see gccint manual section >> - Target Macros::Misc.). >> - Used for CO-RE support builtins such as __builtin_preserve_access_index >> - and __builtin_preserve_field_info. >> - >> - FNDECL is the declaration of the builtin, and ARGLIST is the list of >> - arguments passed to it, and is really a vec<tree,_> *. */ >> - >> -static tree >> -bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist) >> -{ >> - enum bpf_builtins which = (enum bpf_builtins) DECL_MD_FUNCTION_CODE >> (fndecl); >> - >> - if (which < BPF_BUILTIN_PRESERVE_ACCESS_INDEX >> - || which >= BPF_BUILTIN_MAX) >> - return NULL_TREE; >> - >> - vec<tree, va_gc> *params = static_cast<vec<tree, va_gc> *> (arglist); >> - unsigned n_params = params ? params->length() : 0; >> - >> - if (!(which == BPF_BUILTIN_PRESERVE_ACCESS_INDEX && n_params == 1) >> - && n_params != 2) >> - { >> - error_at (loc, "wrong number of arguments"); >> - return error_mark_node; >> - } >> - >> - tree param = (*params)[0]; >> - >> - /* If not generating BPF_CORE information, preserve_access_index does >> - nothing, and simply "resolves to" the argument. */ >> - if (which == BPF_BUILTIN_PRESERVE_ACCESS_INDEX && !TARGET_BPF_CORE) >> - return param; >> - >> - /* For __builtin_preserve_field_info, enforce that the parameter is >> exactly a >> - field access and not a more complex expression. */ >> - else if (which == BPF_BUILTIN_PRESERVE_FIELD_INFO >> - && !bpf_is_valid_preserve_field_info_arg (param)) >> - { >> - error_at (EXPR_LOC_OR_LOC (param, loc), >> - "argument is not a field access"); >> - return error_mark_node; >> - } >> - >> - /* Do remove_c_maybe_const_expr for the arg. >> - TODO: WHY do we have to do this here? Why doesn't c-typeck take care >> - of it before or after this hook? */ >> - if (TREE_CODE (param) == C_MAYBE_CONST_EXPR) >> - param = C_MAYBE_CONST_EXPR_EXPR (param); >> - >> - /* Construct a new function declaration with the correct type, and return >> - a call to it. >> - >> - Calls with statement-expressions, for example: >> - _(({ foo->a = 1; foo->u[2].b = 2; })) >> - require special handling. >> - >> - We rearrange this into a new block scope in which each statement >> - becomes a unique builtin call: >> - { >> - _ ({ foo->a = 1;}); >> - _ ({ foo->u[2].b = 2;}); >> - } >> - >> - This ensures that all the relevant information remains within the >> - expression trees the builtin finally gets. */ >> - >> - struct core_walk_data data; >> - data.loc = loc; >> - data.which = which; >> - if (which == BPF_BUILTIN_PRESERVE_ACCESS_INDEX) >> - data.arg = NULL_TREE; >> - else >> - data.arg = (*params)[1]; >> - >> - walk_tree (¶m, bpf_core_walk, (void *) &data, NULL); >> - >> - return param; >> -} >> - >> -#undef TARGET_RESOLVE_OVERLOADED_BUILTIN >> -#define TARGET_RESOLVE_OVERLOADED_BUILTIN bpf_resolve_overloaded_builtin >> - >> - >> -/* Handling for __attribute__((preserve_access_index)) for BPF CO-RE >> support. >> - >> - This attribute marks a structure/union/array type as "preseve", 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. >> - >> - Note also that nested structs behave as though they all have the >> attribute. >> - For example: >> - struct X { int a; }; >> - struct Y { struct X bar} __attribute__((preserve_access_index)); >> - struct Y foo; >> - foo.bar.a; >> - 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 pass finds all accesses to objects of types marked with the >> attribute, >> - and wraps them in the same "low-level" builtins used by the builtin >> version. >> - All logic afterwards is therefore identical to the builtin version of >> - preserve_access_index. */ >> - >> -/* True iff tree T accesses any member of a struct/union/class which is >> marked >> - with the PRESERVE_ACCESS_INDEX attribute. */ >> - >> -static bool >> -is_attr_preserve_access (tree t) >> -{ >> - if (t == NULL_TREE) >> - return false; >> - >> - poly_int64 bitsize, bitpos; >> - tree var_off; >> - machine_mode mode; >> - int sign, reverse, vol; >> - >> - tree base = get_inner_reference (t, &bitsize, &bitpos, &var_off, &mode, >> - &sign, &reverse, &vol); >> - >> - if (TREE_CODE (base) == MEM_REF) >> - { >> - return lookup_attribute ("preserve_access_index", >> - TYPE_ATTRIBUTES (TREE_TYPE (base))); >> - } >> - >> - if (TREE_CODE (t) == COMPONENT_REF) >> - { >> - /* preserve_access_index propegates into nested structures, >> - so check whether this is a component of another component >> - which in turn is part of such a struct. */ >> - >> - const tree op = TREE_OPERAND (t, 0); >> - >> - if (TREE_CODE (op) == COMPONENT_REF) >> - return is_attr_preserve_access (op); >> - >> - const tree container = DECL_CONTEXT (TREE_OPERAND (t, 1)); >> - >> - return lookup_attribute ("preserve_access_index", >> - TYPE_ATTRIBUTES (container)); >> - } >> - >> - else if (TREE_CODE (t) == ADDR_EXPR) >> - return is_attr_preserve_access (TREE_OPERAND (t, 0)); >> - >> - return false; >> -} >> - >> -/* The body of pass_bpf_core_attr. Scan RTL for accesses to structs/unions >> - marked with __attribute__((preserve_access_index)) and generate a CO-RE >> - relocation for any such access. */ >> - >> -static void >> -handle_attr_preserve (function *fn) >> -{ >> - basic_block bb; >> - rtx_insn *insn; >> - FOR_EACH_BB_FN (bb, fn) >> - { >> - FOR_BB_INSNS (bb, insn) >> - { >> - if (!NONJUMP_INSN_P (insn)) >> - continue; >> - rtx pat = PATTERN (insn); >> - if (GET_CODE (pat) != SET) >> - continue; >> - >> - start_sequence(); >> - >> - for (int i = 0; i < 2; i++) >> - { >> - rtx mem = XEXP (pat, i); >> - if (MEM_P (mem)) >> - { >> - tree expr = MEM_EXPR (mem); >> - if (!expr) >> - continue; >> - >> - 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)) >> - maybe_make_core_relo (expr, BPF_RELO_FIELD_BYTE_OFFSET); >> - } >> - } >> - rtx_insn *seq = get_insns (); >> - end_sequence (); >> - emit_insn_before (seq, insn); >> - } >> - } >> -} >> - >> -/* This pass finds accesses to structures marked with the BPF target >> attribute >> - __attribute__((preserve_access_index)). For every such access, a CO-RE >> - relocation record is generated, to be output in the .BTF.ext section. */ >> - >> -namespace { >> - >> -const pass_data pass_data_bpf_core_attr = >> -{ >> - RTL_PASS, /* type */ >> - "bpf_core_attr", /* name */ >> - OPTGROUP_NONE, /* optinfo_flags */ >> - TV_NONE, /* tv_id */ >> - 0, /* properties_required */ >> - 0, /* properties_provided */ >> - 0, /* properties_destroyed */ >> - 0, /* todo_flags_start */ >> - 0, /* todo_flags_finish */ >> -}; >> - >> -class pass_bpf_core_attr : public rtl_opt_pass >> -{ >> -public: >> - pass_bpf_core_attr (gcc::context *ctxt) >> - : rtl_opt_pass (pass_data_bpf_core_attr, ctxt) >> - {} >> - >> - virtual bool gate (function *) { return TARGET_BPF_CORE; } >> - virtual unsigned int execute (function *); >> -}; >> - >> -unsigned int >> -pass_bpf_core_attr::execute (function *fn) >> -{ >> - handle_attr_preserve (fn); >> - return 0; >> -} >> - >> -} /* Anonymous namespace. */ >> - >> -rtl_opt_pass * >> -make_pass_bpf_core_attr (gcc::context *ctxt) >> -{ >> - return new pass_bpf_core_attr (ctxt); >> -} >> - >> /* Finally, build the GCC target. */ >> >> struct gcc_target targetm = TARGET_INITIALIZER; >> diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md >> index a69a239b9d6a..e9c00e445af3 100644 >> --- a/gcc/config/bpf/bpf.md >> +++ b/gcc/config/bpf/bpf.md >> @@ -45,6 +45,7 @@ >> UNSPEC_AFXOR >> UNSPEC_AXCHG >> UNSPEC_ACMP >> + UNSPEC_CORE_RELOC >> ]) >> >> ;;;; Constants >> @@ -367,6 +368,8 @@ >> "" >> " >> { >> + 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]); >> @@ -384,6 +387,20 @@ >> {st<mop>\t%0,%1|*(<smop> *) (%0) = %1}" >> [(set_attr "type" "ldx,alu,alu,stx,st")]) >> >> +(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") >> + (match_operand:SI 2 "immediate_operand" " I,I,I") >> + ] UNSPEC_CORE_RELOC) >> + )] >> + "" >> + "@ >> + *return bpf_add_core_reloc (operands, \"{mov\t%0,%1|%0 = %1}\"); >> + *return bpf_add_core_reloc (operands, \"{st<mop>\t%0,%1|*(<smop> *) (%0) >> = %1}\"); >> + *return bpf_add_core_reloc (operands, \"{lddw\t%0,%1|%0 = %1 ll}\");" >> + [(set_attr "type" "alu,st,alu")]) >> + >> ;;;; Shifts >> >> (define_mode_iterator SIM [(SI "bpf_has_alu32") DI]) >> diff --git a/gcc/config/bpf/core-builtins.cc >> b/gcc/config/bpf/core-builtins.cc >> new file mode 100644 >> index 000000000000..575e63d8ea77 >> --- /dev/null >> +++ b/gcc/config/bpf/core-builtins.cc >> @@ -0,0 +1,1394 @@ >> +/* Subroutines used for code generation for eBPF. >> + Copyright (C) 2019-2023 Free Software Foundation, Inc. >> + >> +This file is part of GCC. >> + >> +GCC is free software; you can redistribute it and/or modify >> +it under the terms of the GNU General Public License as published by >> +the Free Software Foundation; either version 3, or (at your option) >> +any later version. >> + >> +GCC is distributed in the hope that it will be useful, >> +but WITHOUT ANY WARRANTY; without even the implied warranty of >> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> +GNU General Public License for more details. >> + >> +You should have received a copy of the GNU General Public License >> +along with GCC; see the file COPYING3. If not see >> +<http://www.gnu.org/licenses/>. */ >> + >> +#define IN_TARGET_CODE 1 >> + >> +#include "config.h" >> +#include "system.h" >> +#include "coretypes.h" >> +#include "tm.h" >> +#include "rtl.h" >> +#include "regs.h" >> +#include "insn-config.h" >> +#include "insn-attr.h" >> +#include "recog.h" >> +#include "output.h" >> +#include "alias.h" >> +#include "tree.h" >> +#include "stringpool.h" >> +#include "attribs.h" >> +#include "varasm.h" >> +#include "stor-layout.h" >> +#include "calls.h" >> +#include "function.h" >> +#include "explow.h" >> +#include "memmodel.h" >> +#include "emit-rtl.h" >> +#include "reload.h" >> +#include "tm_p.h" >> +#include "target.h" >> +#include "basic-block.h" >> +#include "expr.h" >> +#include "optabs.h" >> +#include "bitmap.h" >> +#include "df.h" >> +#include "c-family/c-common.h" >> +#include "diagnostic.h" >> +#include "builtins.h" >> +#include "predict.h" >> +#include "langhooks.h" >> +#include "flags.h" >> + >> +#include "cfg.h" >> +#include "gimple.h" >> +#include "gimple-iterator.h" >> +#include "gimple-walk.h" >> +#include "tree-pass.h" >> +#include "tree-iterator.h" >> + >> +#include "context.h" >> +#include "pass_manager.h" >> + >> +#include "gimplify.h" >> +#include "gimplify-me.h" >> + >> +#include "plugin.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. */ >> + >> + >> +struct cr_builtins >> +{ >> + tree type; >> + tree expr; >> + tree default_value; >> + rtx rtx_default_value; >> + enum btf_core_reloc_kind kind; /* Recovered from proper argument. */ >> + enum bpf_builtins orig_builtin_code; >> + tree orig_arg_expr; >> +}; >> + >> +#define CORE_BUILTINS_DATA_EMPTY \ >> + { NULL_TREE, NULL_TREE, NULL_TREE, NULL_RTX, BPF_RELO_INVALID, \ >> + BPF_BUILTIN_UNUSED, NULL } >> + >> +/* Vector definition and its access function. */ >> +vec<struct cr_builtins> builtins_data; >> + >> +static inline int >> +allocate_builtin_data () >> +{ >> + struct cr_builtins data = CORE_BUILTINS_DATA_EMPTY; >> + int ret = builtins_data.length (); >> + builtins_data.safe_push (data); >> + return ret; >> +} >> + >> +static inline struct cr_builtins * >> +get_builtin_data (int index) >> +{ >> + return &builtins_data[index]; >> +} >> + >> +typedef bool >> +(*builtin_local_data_compare_fn) (struct cr_builtins *a, >> + struct cr_builtins *b); >> +static inline int >> +search_builtin_data (builtin_local_data_compare_fn callback, >> + struct cr_builtins *elem) >> +{ >> + unsigned int i; >> + for (i = 0; i < builtins_data.length (); i++) >> + if ((callback != NULL && (callback) (elem, &builtins_data[i])) >> + || (callback == NULL >> + && (builtins_data[i].orig_arg_expr == elem->orig_arg_expr))) >> + return (int) i; >> + >> + return -1; >> +} >> + >> +/* Possible relocation decisions. */ >> +enum cr_decision >> +{ >> + FAILED_VALIDATION = 0, >> + KEEP_ORIGINAL_NO_RELOCATION, >> + REPLACE_CREATE_RELOCATION, >> + REPLACE_NO_RELOCATION >> +}; >> + >> +/* Core Relocation Pack local structure. */ >> +struct cr_local >> +{ >> + struct cr_builtins reloc_data; >> + enum cr_decision reloc_decision; >> + bool fail; >> +}; >> +#define CR_LOCAL_EMPTY { CORE_BUILTINS_DATA_EMPTY, FAILED_VALIDATION, false >> } >> + >> +/* Core Relocation Final data */ >> +struct cr_final >> +{ >> + char *str; >> + tree type; >> + enum btf_core_reloc_kind kind; >> +}; >> + >> +/* CO-RE builtin helpers struct. Used and initialized in >> + bpf_init_core_builtins. */ >> +struct builtin_helpers >> +{ >> + enum btf_core_reloc_kind (*kind) (tree *args, int nargs); >> + bool (*compare) (struct cr_builtins *a, struct cr_builtins *b); >> + struct cr_local (*pack) (tree *args, >> + enum btf_core_reloc_kind kind, >> + enum bpf_builtins code); >> + struct cr_final (*process) (struct cr_builtins *data); >> + bool is_pure; >> + bool is_valid; >> +}; >> + >> +struct builtin_helpers >> + core_builtin_helpers[(int) BPF_BUILTIN_MAX]; >> + >> +#define BPF_CORE_HELPER_NOTSET { NULL, NULL, NULL, NULL, false, false } >> +#define BPF_CORE_HELPER_SET(KIND, COMPARE, PACK, PROCESS, IS_PURE) \ >> + { KIND, COMPARE, PACK, PROCESS, IS_PURE, true } >> + >> +enum bpf_plugin_states >> +{ >> + BPF_PLUGIN_DISABLED = 0, >> + BPF_PLUGIN_ENABLED, >> + BPF_PLUGIN_REMOVED >> +}; >> +enum bpf_plugin_states plugin_state = BPF_PLUGIN_DISABLED; >> + >> +static void >> +remove_parser_plugin () >> +{ >> + /* Restore state of the plugin system. */ >> + if (flag_plugin_added == true && plugin_state != BPF_PLUGIN_REMOVED) >> + { >> + unregister_callback ("bpf_collect_enum_info", PLUGIN_FINISH_TYPE); >> + flag_plugin_added = (bool) plugin_state == BPF_PLUGIN_ENABLED; >> + plugin_state = BPF_PLUGIN_REMOVED; >> + } >> +} >> + >> +#define bpf_error(MSG) { \ >> + remove_parser_plugin (); \ >> + error (MSG); \ >> +} >> + >> +#define bpf_error_at(LOC, MSG) { \ >> + remove_parser_plugin (); \ >> + error_at (LOC, MSG); \ >> +} >> + >> + >> +/* Helper compare functions used to verify if multiple builtin calls contain >> + the same argument as input. In that case the builtin calls can be >> optimized >> + out by identifying redundat calls. This happen since the internal >> + __core_reloc builtin is marked as PURE. */ >> + >> +static inline bool >> +compare_same_kind (struct cr_builtins *a, struct cr_builtins *b) >> +{ >> + return a->kind == b->kind; >> +} >> +static inline bool >> +compare_same_ptr_expr (struct cr_builtins *a, struct cr_builtins *b) >> +{ >> + return compare_same_kind (a, b) && a->expr == b->expr; >> +} >> +static inline bool >> +compare_same_ptr_type (struct cr_builtins *a, struct cr_builtins *b) >> +{ >> + return compare_same_kind (a, b) && a->type == b->type; >> +} >> + >> +/* Handling for __attribute__((preserve_access_index)) for BPF CO-RE >> support. >> + >> + This attribute marks a structure/union/array type as "preseve", 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. >> + >> + Note also that nested structs behave as though they all have the >> attribute. >> + For example: >> + struct X { int a; }; >> + struct Y { struct X bar} __attribute__((preserve_access_index)); >> + struct Y foo; >> + foo.bar.a; >> + 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. */ >> + >> +/* True if tree T accesses any member of a struct/union/class which is >> marked >> + with the PRESERVE_ACCESS_INDEX attribute. */ >> + >> +static bool >> +is_attr_preserve_access (tree t) >> +{ >> + if (t == NULL_TREE) >> + return false; >> + >> + poly_int64 bitsize, bitpos; >> + tree var_off; >> + machine_mode mode; >> + int sign, reverse, vol; >> + >> + tree base = get_inner_reference (t, &bitsize, &bitpos, &var_off, &mode, >> + &sign, &reverse, &vol); >> + >> + if (TREE_CODE (base) == MEM_REF) >> + { >> + return lookup_attribute ("preserve_access_index", >> + TYPE_ATTRIBUTES (TREE_TYPE (base))); >> + } >> + >> + if (TREE_CODE (t) == COMPONENT_REF) >> + { >> + /* preserve_access_index propagates into nested structures, >> + so check whether this is a component of another component >> + which in turn is part of such a struct. */ >> + >> + const tree op = TREE_OPERAND (t, 0); >> + >> + if (TREE_CODE (op) == COMPONENT_REF) >> + return is_attr_preserve_access (op); >> + >> + const tree container = DECL_CONTEXT (TREE_OPERAND (t, 1)); >> + >> + return lookup_attribute ("preserve_access_index", >> + TYPE_ATTRIBUTES (container)); >> + } >> + >> + else if (TREE_CODE (t) == ADDR_EXPR) >> + return is_attr_preserve_access (TREE_OPERAND (t, 0)); >> + >> + return false; >> +} >> + >> + >> +/* Expand a call to __builtin_preserve_field_info by evaluating the >> requested >> + information about SRC according to KIND, and return a tree holding >> + the result. */ >> + >> +static tree >> +core_field_info (tree src, enum btf_core_reloc_kind kind) >> +{ >> + unsigned int result; >> + poly_int64 bitsize, bitpos; >> + tree var_off = NULL_TREE; >> + machine_mode mode; >> + int unsignedp, reversep, volatilep; >> + location_t loc = EXPR_LOCATION (src); >> + tree type = TREE_TYPE (src); >> + >> + get_inner_reference (src, &bitsize, &bitpos, &var_off, &mode, &unsignedp, >> + &reversep, &volatilep); >> + >> + /* Note: Use DECL_BIT_FIELD_TYPE rather than DECL_BIT_FIELD here, because >> it >> + remembers whether the field in question was originally declared as a >> + bitfield, regardless of how it has been optimized. */ >> + bool bitfieldp = (TREE_CODE (src) == COMPONENT_REF >> + && DECL_BIT_FIELD_TYPE (TREE_OPERAND (src, 1))); >> + >> + unsigned int align = TYPE_ALIGN (TREE_TYPE (src)); >> + if (TREE_CODE (src) == COMPONENT_REF) >> + { >> + tree field = TREE_OPERAND (src, 1); >> + if (DECL_BIT_FIELD_TYPE (field)) >> + align = TYPE_ALIGN (DECL_BIT_FIELD_TYPE (field)); >> + else >> + align = TYPE_ALIGN (TREE_TYPE (field)); >> + } >> + >> + unsigned int start_bitpos = bitpos & ~(align - 1); >> + unsigned int end_bitpos = start_bitpos + align; >> + >> + switch (kind) >> + { >> + case BPF_RELO_FIELD_BYTE_OFFSET: >> + { >> + type = unsigned_type_node; >> + if (var_off != NULL_TREE) >> + { >> + bpf_error_at (loc, "unsupported variable field offset"); >> + return error_mark_node; >> + } >> + >> + if (bitfieldp) >> + result = start_bitpos / 8; >> + else >> + result = bitpos / 8; >> + } >> + break; >> + >> + case BPF_RELO_FIELD_BYTE_SIZE: >> + { >> + type = unsigned_type_node; >> + if (mode == BLKmode && bitsize == -1) >> + { >> + bpf_error_at (loc, "unsupported variable size field access"); >> + return error_mark_node; >> + } >> + >> + if (bitfieldp) >> + { >> + /* To match LLVM behavior, byte size of bitfields is recorded as >> + the full size of the base type. A 3-bit bitfield of type int is >> + therefore recorded as having a byte size of 4 bytes. */ >> + result = end_bitpos - start_bitpos; >> + if (result & (result - 1)) >> + { >> + bpf_error_at (loc, "unsupported field expression"); >> + return error_mark_node; >> + } >> + result = result / 8; >> + } >> + else >> + result = bitsize / 8; >> + } >> + break; >> + >> + case BPF_RELO_FIELD_EXISTS: >> + type = unsigned_type_node; >> + /* The field always exists at compile time. */ >> + result = 1; >> + break; >> + >> + case BPF_RELO_FIELD_SIGNED: >> + type = unsigned_type_node; >> + result = !unsignedp; >> + break; >> + >> + case BPF_RELO_FIELD_LSHIFT_U64: >> + case BPF_RELO_FIELD_RSHIFT_U64: >> + { >> + type = unsigned_type_node; >> + if (mode == BLKmode && bitsize == -1) >> + { >> + bpf_error_at (loc, "unsupported variable size field access"); >> + return error_mark_node; >> + } >> + if (var_off != NULL_TREE) >> + { >> + bpf_error_at (loc, "unsupported variable field offset"); >> + return error_mark_node; >> + } >> + >> + if (!bitfieldp) >> + { >> + if (bitsize > 64) >> + { >> + bpf_error_at (loc, "field size too large"); >> + return error_mark_node; >> + } >> + result = 64 - bitsize; >> + break; >> + } >> + >> + if (end_bitpos - start_bitpos > 64) >> + { >> + bpf_error_at (loc, "field size too large"); >> + return error_mark_node; >> + } >> + >> + if (kind == BPF_RELO_FIELD_LSHIFT_U64) >> + { >> + if (TARGET_BIG_ENDIAN) >> + result = bitpos + 64 - start_bitpos - align; >> + else >> + result = start_bitpos + 64 - bitpos - bitsize; >> + } >> + else /* RSHIFT_U64 */ >> + result = 64 - bitsize; >> + } >> + break; >> + >> + default: >> + bpf_error ("invalid second argument to built-in function"); >> + return error_mark_node; >> + break; >> + } >> + >> + return build_int_cst (type, result); >> +} >> + >> +/* Compute the index of the NODE in its immediate container. >> + 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) >> +{ >> + enum tree_code code = TREE_CODE (node); >> + >> + if (code == FIELD_DECL) >> + { >> + /* Lookup the index from the type fields information. */ >> + const tree container = DECL_CONTEXT (node); >> + int i = 0; >> + for (tree l = TYPE_FIELDS (container); l; l = DECL_CHAIN (l)) >> + { >> + if (l == node) >> + return i; >> + i++; >> + } >> + } >> + else if (code == ARRAY_REF || code == ARRAY_RANGE_REF || code == MEM_REF) >> + { >> + /* For array accesses, the index is operand 1. */ >> + tree index = TREE_OPERAND (node, 1); >> + >> + /* If the indexing operand is a constant, extracting is trivial. */ >> + if (TREE_CODE (index) == INTEGER_CST && tree_fits_shwi_p (index)) >> + return tree_to_shwi (index); >> + } >> + else if (code == POINTER_PLUS_EXPR) >> + { >> + tree offset = TREE_OPERAND (node, 1); >> + tree type = TREE_TYPE (TREE_OPERAND (node, 0)); >> + >> + if (TREE_CODE (offset) == INTEGER_CST && tree_fits_shwi_p (offset) >> + && COMPLETE_TYPE_P (type) && tree_fits_shwi_p (TYPE_SIZE (type))) >> + { >> + HOST_WIDE_INT offset_i = tree_to_shwi (offset); >> + HOST_WIDE_INT type_size_i = tree_to_shwi (TYPE_SIZE_UNIT (type)); >> + if ((offset_i % type_size_i) == 0) >> + return offset_i / type_size_i; >> + } >> + } >> + >> + gcc_unreachable (); >> + 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. */ >> + >> +static unsigned char >> +compute_field_expr (tree node, unsigned int *accessors, bool *valid, >> + tree *root) >> +{ >> + unsigned char n = 0; >> + if (node == NULL_TREE) >> + { >> + *valid = false; >> + return 0; >> + } >> + >> + switch (TREE_CODE (node)) >> + { >> + case INDIRECT_REF: >> + case ADDR_EXPR: >> + accessors[0] = 0; >> + n = compute_field_expr (TREE_OPERAND (node, 0), &accessors[0], valid, >> + root); >> + *root = node; >> + return n + 1; >> + case POINTER_PLUS_EXPR: >> + accessors[0] = bpf_core_get_index (node); >> + *root = node; >> + 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; >> + 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; >> + case NOP_EXPR: >> + n = compute_field_expr (TREE_OPERAND (node, 0), accessors, valid, >> root); >> + *root = node; >> + 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 SSA_NAME: >> + case VAR_DECL: >> + case PARM_DECL: >> + return 0; >> + default: >> + *valid = false; >> + return 0; >> + } >> +} >> + >> +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; >> +} >> + >> +static struct cr_local >> +pack_field_expr_for_preserve_field (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; >> + >> + /* 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; >> + >> + 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), >> + "argument is not a field access"); >> + 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; >> +} >> + >> +static struct cr_final >> +process_field_expr (struct cr_builtins *data) >> +{ >> + gcc_assert (data->kind == BPF_RELO_FIELD_BYTE_OFFSET >> + || data->kind == BPF_RELO_FIELD_BYTE_SIZE >> + || data->kind == BPF_RELO_FIELD_LSHIFT_U64 >> + || data->kind == BPF_RELO_FIELD_RSHIFT_U64 >> + || data->kind == BPF_RELO_FIELD_SIGNED >> + || data->kind == BPF_RELO_FIELD_EXISTS); >> + >> + unsigned int accessors[100]; >> + unsigned char nr_accessors = 0; >> + bool valid = true; >> + tree root = NULL_TREE; >> + tree expr = data->expr; >> + tree type = TREE_TYPE (data->expr); >> + >> + if (TREE_CODE (expr) == ADDR_EXPR) >> + expr = TREE_OPERAND (expr, 0); >> + >> + nr_accessors = compute_field_expr (expr, accessors, &valid, &root); >> + >> + struct cr_final ret = { NULL, type, data->kind}; >> + >> + char str[100]; >> + if (nr_accessors > 0) >> + { >> + int n = 0; >> + for (int i = 0; i < nr_accessors; i++) >> + n += snprintf (str + n, sizeof (str) - n, >> + i == 0 ? "%u" : ":%u", accessors[i]); >> + ret.str = CONST_CAST (char *, ggc_strdup (str)); >> + } >> + else >> + gcc_unreachable (); >> + >> + return ret; >> +} >> + >> +hash_map <tree, tree> bpf_enum_mappings; >> + >> +tree enum_value_type = NULL_TREE; >> +static struct cr_local >> +pack_enum_value (tree *args, enum btf_core_reloc_kind kind, >> + enum bpf_builtins code ATTRIBUTE_UNUSED) >> +{ >> + struct cr_local ret = CR_LOCAL_EMPTY; >> + ret.reloc_decision = REPLACE_CREATE_RELOCATION; >> + ret.fail = false; >> + >> + tree *result = NULL; >> + tree tmp = args[0]; >> + tree enum_value = args[1]; >> + tree type = NULL_TREE; >> + >> + /* Deconstructing "*(typeof (enum_type) *) enum_value" to collect both the >> + * enum_type and enum_value. */ >> + if (TREE_CODE (tmp) != TARGET_EXPR >> + || (type = TREE_TYPE (tmp)) == NULL_TREE >> + || (TREE_CODE (type) != POINTER_TYPE) >> + || (type = TREE_TYPE (type)) == NULL_TREE >> + || (TREE_CODE (type) != ENUMERAL_TYPE)) >> + { >> + bpf_error ("invalid type argument format for enum value builtin"); >> + ret.fail = true; >> + } >> + >> + if (TREE_CODE (enum_value) != INTEGER_CST) >> + goto pack_enum_value_fail; >> + >> + result = bpf_enum_mappings.get (enum_value); >> + if (result == NULL) >> + goto pack_enum_value_fail; >> + >> + tmp = *result; >> + >> + if (TREE_CODE (tmp) != CONST_DECL) >> + { >> +pack_enum_value_fail: >> + bpf_error ("invalid enum value argument for enum value builtin"); >> + ret.fail = true; >> + } >> + else >> + { >> + ret.reloc_data.expr = tmp; >> + if (kind == BPF_RELO_ENUMVAL_VALUE) >> + ret.reloc_data.default_value = enum_value; >> + else >> + ret.reloc_data.default_value = integer_one_node; >> + } >> + >> + ret.reloc_data.type = type; >> + ret.reloc_data.kind = kind; >> + return ret; >> +} >> + >> +static struct cr_final >> +process_enum_value (struct cr_builtins *data) >> +{ >> + gcc_assert (data->kind == BPF_RELO_ENUMVAL_EXISTS >> + || data->kind == BPF_RELO_ENUMVAL_VALUE); >> + >> + tree expr = data->expr; >> + tree type = data->type; >> + >> + struct cr_final ret = { NULL, type, data->kind }; >> + >> + if (TREE_CODE (expr) == CONST_DECL >> + && TREE_CODE (type) == ENUMERAL_TYPE) >> + { >> + unsigned int index = 0; >> + for (tree l = TYPE_VALUES (type); l; l = TREE_CHAIN (l)) >> + { >> + if (TREE_VALUE (l) == expr) >> + { >> + ret.str = (char *) ggc_alloc_atomic ((index / 10) + 1); >> + sprintf (ret.str, "%d", index); >> + break; >> + } >> + index++; >> + } >> + } >> + else >> + gcc_unreachable (); >> + >> + return ret; >> +} >> + >> +static struct cr_local >> +pack_type (tree *args, enum btf_core_reloc_kind kind, >> + enum bpf_builtins code ATTRIBUTE_UNUSED) >> +{ >> + struct cr_local ret = CR_LOCAL_EMPTY; >> + ret.reloc_decision = FAILED_VALIDATION; >> + ret.reloc_data.default_value = integer_zero_node; >> + ret.fail = false; >> + >> + tree root_type = NULL_TREE; >> + tree tmp = args[0]; >> + HOST_WIDE_INT type_size_i; >> + >> + /* Typical structure to match: >> + * *({ 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)) >> + tmp = TREE_OPERAND (tmp, 0); >> + >> + if (TREE_CODE (tmp) != TARGET_EXPR >> + || TREE_CODE (tmp = TREE_OPERAND (tmp, 1)) != BIND_EXPR) >> + goto pack_type_fail; >> + >> + tmp = BIND_EXPR_VARS (tmp); >> + >> + if (TREE_CODE (tmp) != TYPE_DECL >> + && TREE_CODE (tmp) != VAR_DECL) >> + goto pack_type_fail; >> + >> + tmp = TREE_TYPE (tmp); >> + >> + if (TREE_CODE (tmp) == POINTER_TYPE) >> + tmp = TREE_TYPE (tmp); >> + >> + root_type = tmp; >> + >> + if (TREE_CODE (tmp) != RECORD_TYPE >> + && TREE_CODE (tmp) != UNION_TYPE >> + && TREE_CODE (tmp) != ENUMERAL_TYPE >> + && (TREE_CODE (tmp) != POINTER_TYPE >> + || TREE_CODE (TREE_TYPE (tmp)) == FUNCTION_TYPE) >> + && (TREE_CODE (tmp) != POINTER_TYPE >> + || TREE_CODE (TREE_TYPE (tmp)) == VOID_TYPE) >> + && TREE_CODE (tmp) != ARRAY_TYPE >> + && TREE_CODE (tmp) != INTEGER_TYPE) >> + goto pack_type_fail; >> + >> + ret.reloc_data.type = root_type; >> + ret.reloc_decision = REPLACE_CREATE_RELOCATION; >> + >> + /* 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); >> + cfun->used_types_hash->add (root_type); >> + >> + type_size_i = tree_to_shwi (TYPE_SIZE_UNIT (ret.reloc_data.type)); >> + >> + switch (kind) >> + { >> + case BPF_RELO_TYPE_SIZE: >> + ret.reloc_data.default_value = build_int_cst (integer_type_node, >> + type_size_i); >> + break; >> + case BPF_RELO_TYPE_EXISTS: >> + case BPF_RELO_TYPE_MATCHES: >> + ret.reloc_data.default_value = integer_one_node; >> + break; >> + case BPF_RELO_TYPE_ID_LOCAL: >> + case BPF_RELO_TYPE_ID_TARGET: >> + ret.reloc_data.default_value = integer_zero_node; >> + break; >> + default: >> + break; >> + } >> + >> + ret.reloc_data.kind = kind; >> + return ret; >> + >> +pack_type_fail: >> + bpf_error_at (EXPR_LOC_OR_LOC (args[0], UNKNOWN_LOCATION), >> + "invelid first argument format for enum value builtin"); >> + ret.fail = true; >> + return ret; >> +} >> + >> +static struct cr_final >> +process_type (struct cr_builtins *data) >> +{ >> + gcc_assert (data->kind == BPF_RELO_TYPE_ID_LOCAL >> + || data->kind == BPF_RELO_TYPE_ID_TARGET >> + || data->kind == BPF_RELO_TYPE_EXISTS >> + || data->kind == BPF_RELO_TYPE_SIZE >> + || data->kind == BPF_RELO_TYPE_MATCHES); >> + >> + struct cr_final ret; >> + ret.str = NULL; >> + ret.type = data->type; >> + ret.kind = data->kind; >> + >> + if ((data->kind == BPF_RELO_TYPE_ID_LOCAL >> + || data->kind == BPF_RELO_TYPE_ID_TARGET) >> + && data->default_value != NULL) >> + { >> + ctf_container_ref ctfc = ctf_get_tu_ctfc (); >> + unsigned int btf_id = get_btf_id (ctf_lookup_tree_type (ctfc, >> ret.type)); >> + data->rtx_default_value = expand_normal (build_int_cst >> (integer_type_node, >> + btf_id)); >> + } >> + >> + return ret; >> +} >> + >> +static bool >> +bpf_require_core_support () >> +{ >> + if (!TARGET_BPF_CORE) >> + { >> + bpf_error ("BPF CO-RE is required but not enabled"); >> + return false; >> + } >> + return true; >> +} >> + >> +/* BPF Compile Once - Run Everywhere (CO-RE) support. Construct a CO-RE >> + relocation record in DATA to be emitted in the .BTF.ext >> + section. Does nothing if we are not targetting BPF CO-RE, or if the >> + constructed relocation would be a no-op. */ >> + >> +static void >> +make_core_relo (struct cr_final *data, rtx_code_label *label) >> +{ >> + /* If we are not targetting BPF CO-RE, do not make a relocation. We >> + might not be generating any debug info at all. */ >> + if (!bpf_require_core_support ()) >> + return; >> + >> + gcc_assert (data->type); >> + >> + /* 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; >> + if (current_function_decl && DECL_SECTION_NAME (current_function_decl)) >> + section_name = DECL_SECTION_NAME (current_function_decl); >> + else >> + section_name = ".text"; >> + >> + /* Add the CO-RE relocation information to the BTF container. */ >> + bpf_core_reloc_add (data->type, section_name, data->str, label, >> + data->kind); >> +} >> + >> +/* Support function to extract kind information for CO-RE builtin >> + calls. */ >> + >> +static inline char >> +read_kind (tree kind, char max_value, char enum_offset) >> +{ >> + char kind_val = 0; >> + >> + if (kind == NULL_TREE) >> + goto invalid_kind_arg_error; >> + >> + if (TREE_CODE (kind) != CONST_DECL >> + && TREE_CODE (kind) == NOP_EXPR) >> + kind = TREE_OPERAND (kind, 0); >> + >> + if (TREE_CODE (kind) == CONST_DECL) >> + kind = DECL_INITIAL (kind); >> + >> + if (TREE_CODE (kind) == INTEGER_CST >> + && tree_fits_uhwi_p (kind)) >> + kind_val = tree_to_uhwi (kind); >> + else >> + goto invalid_kind_arg_error; >> + >> + if (kind_val > max_value) >> + { >> +invalid_kind_arg_error: >> + bpf_error ("invalid kind argument to core builtin"); >> + return -1; >> + } >> + return kind_val + enum_offset; >> +} >> + >> +#define KIND_EXPECT_NARGS(N, MSG) \ >> + { if (nargs != N) { bpf_error (MSG); return BPF_RELO_INVALID; } } >> + >> +/* Helper functions to extract kind information. */ >> +static inline enum btf_core_reloc_kind >> +kind_access_index (tree *args ATTRIBUTE_UNUSED, int nargs) >> +{ >> + KIND_EXPECT_NARGS (1, >> + "wrong number of arguments for access index core builtin"); >> + return BPF_RELO_FIELD_BYTE_OFFSET; >> +} >> +static inline enum btf_core_reloc_kind >> +kind_preserve_field_info (tree *args, int nargs) >> +{ >> + KIND_EXPECT_NARGS (2, >> + "wrong number of arguments for field info core builtin"); >> + return (enum btf_core_reloc_kind) read_kind (args[1], 5, >> + BPF_RELO_FIELD_BYTE_OFFSET); >> +} >> +static inline enum btf_core_reloc_kind >> +kind_enum_value (tree *args, int nargs) >> +{ >> + KIND_EXPECT_NARGS (3, >> + "wrong number of arguments for enum value core builtin"); >> + return (enum btf_core_reloc_kind) read_kind (args[2], 1, >> + BPF_RELO_ENUMVAL_EXISTS); >> +} >> +static inline enum btf_core_reloc_kind >> +kind_type_id (tree *args, int nargs) >> +{ >> + KIND_EXPECT_NARGS (2, >> + "wrong number of arguments for type id core builtin"); >> + return (enum btf_core_reloc_kind) read_kind (args[1], 1, >> + BPF_RELO_TYPE_ID_LOCAL); >> +} >> +static inline enum btf_core_reloc_kind >> +kind_preserve_type_info (tree *args, int nargs) >> +{ >> + KIND_EXPECT_NARGS (2, >> + "wrong number of arguments for type info core builtin"); >> + char val = read_kind (args[1], 2, 0); >> + switch (val) >> + { >> + case 0: >> + return BPF_RELO_TYPE_EXISTS; >> + case 1: >> + return BPF_RELO_TYPE_SIZE; >> + case 2: >> + return BPF_RELO_TYPE_MATCHES; >> + default: >> + break; >> + } >> + 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; >> +} >> + >> +void >> +bpf_handle_plugin_finish_type (void *event_data, >> + void *data ATTRIBUTE_UNUSED) >> +{ >> + tree type = (tree) event_data; >> + >> + if (TREE_CODE (type) == ENUMERAL_TYPE) >> + for (tree l = TYPE_VALUES (type); l; l = TREE_CHAIN (l)) >> + { >> + tree value = TREE_VALUE (l); >> + >> + tree initial = DECL_INITIAL (value); >> + initial = copy_node (initial); >> + DECL_INITIAL (value) = initial; >> + >> + 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. */ >> + >> +void >> +bpf_init_core_builtins (void) >> +{ >> + memset (core_builtin_helpers, 0, sizeof (core_builtin_helpers)); >> + >> + 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, >> + true); >> + core_builtin_helpers[BPF_BUILTIN_PRESERVE_FIELD_INFO] = >> + BPF_CORE_HELPER_SET (kind_preserve_field_info, >> + NULL, >> + pack_field_expr_for_preserve_field, >> + process_field_expr, >> + true); >> + core_builtin_helpers[BPF_BUILTIN_BTF_TYPE_ID] = >> + BPF_CORE_HELPER_SET (kind_type_id, >> + compare_same_ptr_type, >> + pack_type, >> + process_type, >> + true); >> + >> + core_builtin_helpers[BPF_BUILTIN_PRESERVE_TYPE_INFO] = >> + BPF_CORE_HELPER_SET (kind_preserve_type_info, >> + compare_same_ptr_type, >> + pack_type, >> + process_type, >> + true); >> + >> + core_builtin_helpers[BPF_BUILTIN_PRESERVE_ENUM_VALUE] = >> + BPF_CORE_HELPER_SET (kind_enum_value, >> + compare_same_ptr_expr, >> + pack_enum_value, >> + process_enum_value, >> + true); >> + >> + core_builtin_helpers[BPF_BUILTIN_CORE_RELOC] = >> + BPF_CORE_HELPER_SET (NULL, NULL, NULL, NULL, true); >> + >> + /* Initialize plugin handler to record enums value for use in >> + * __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); >> +} >> + >> +static tree >> +construct_builtin_core_reloc (location_t loc, tree fndecl, tree *args, >> + int nargs) >> +{ >> + int code = DECL_MD_FUNCTION_CODE (fndecl); >> + builtin_helpers helper = core_builtin_helpers[code]; >> + >> + if (helper.is_valid) >> + { >> + gcc_assert (helper.kind); >> + gcc_assert (helper.pack); >> + gcc_assert (helper.process); >> + >> + struct cr_local local_data = CR_LOCAL_EMPTY; >> + local_data.fail = false; >> + >> + enum btf_core_reloc_kind kind = helper.kind (args, nargs); >> + if (kind == BPF_RELO_INVALID) >> + local_data.fail = true; >> + else if (helper.pack != NULL) >> + { >> + local_data = helper.pack (args, kind, (enum bpf_builtins) code); >> + local_data.reloc_data.orig_builtin_code = (enum bpf_builtins) code; >> + local_data.reloc_data.orig_arg_expr = args[0]; >> + } >> + else >> + local_data.reloc_decision = KEEP_ORIGINAL_NO_RELOCATION; >> + >> + 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) >> + { >> + int index = search_builtin_data (helper.compare, >> + &local_data.reloc_data); >> + if (index == -1) >> + index = allocate_builtin_data (); >> + 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 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)); >> + } >> + } >> + 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. */ >> + >> +#define MAX_CORE_BUILTIN_ARGS 3 >> +tree >> +bpf_resolve_overloaded_core_builtin (location_t loc, tree fndecl, >> + void *arglist) >> +{ >> + if (!bpf_require_core_support ()) >> + return error_mark_node; >> + >> + vec<tree, va_gc> *argsvec = static_cast<vec<tree, va_gc> *> (arglist); >> + tree args[MAX_CORE_BUILTIN_ARGS]; >> + for (unsigned int i = 0; i < argsvec->length (); i++) >> + args[i] = (*argsvec)[i]; >> + >> + remove_parser_plugin (); >> + >> + 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. */ >> + >> +rtx >> +bpf_expand_core_builtin (tree exp, enum bpf_builtins code) >> +{ >> + if (code == 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); >> + } >> + >> + return NULL_RTX; >> +} >> + >> +/* This function is called in the final assembly output for the >> + 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. */ >> + >> +const char * >> +bpf_add_core_reloc (rtx *operands, const char *templ) >> +{ >> + struct cr_builtins *data = get_builtin_data (INTVAL (operands[2])); >> + builtin_helpers helper; >> + helper = core_builtin_helpers[data->orig_builtin_code]; >> + >> + rtx_code_label * tmp_label = gen_label_rtx (); >> + output_asm_label (tmp_label); >> + assemble_name (asm_out_file, ":\n"); >> + >> + gcc_assert (helper.process != NULL); >> + struct cr_final reloc_data = helper.process (data); >> + make_core_relo (&reloc_data, tmp_label); >> + >> + /* Replace default value for later processing builtin types. >> + 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. */ >> + >> +void >> +bpf_replace_core_move_operands (rtx *operands) >> +{ >> + for (int i = 0; i < 2; i++) >> + if (MEM_P (operands[i])) >> + { >> + tree expr = MEM_EXPR (operands[i]); >> + >> + if (expr == NULL_TREE) >> + continue; >> + >> + 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); >> + >> + 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; >> + >> + int index = allocate_builtin_data (); >> + struct cr_builtins *data = get_builtin_data (index); >> + memcpy (data, &local_data.reloc_data, sizeof (struct cr_builtins)); >> + >> + 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); >> + } >> + >> + emit_insn ( >> + gen_mov_reloc_coredi (reg, >> + gen_rtx_CONST_INT (Pmode, 0), >> + gen_rtx_CONST_INT (Pmode, index))); >> + return; >> + } >> + } >> +} >> diff --git a/gcc/config/bpf/core-builtins.h b/gcc/config/bpf/core-builtins.h >> new file mode 100644 >> index 000000000000..15cd3d3a94e5 >> --- /dev/null >> +++ b/gcc/config/bpf/core-builtins.h >> @@ -0,0 +1,35 @@ >> +#ifndef BPF_CORE_BUILTINS_H >> +#define BPF_CORE_BUILTINS_H >> + >> +#include "coreout.h" >> + >> +enum bpf_builtins >> +{ >> + BPF_BUILTIN_UNUSED = 0, >> + /* Built-ins for non-generic loads and stores. */ >> + BPF_BUILTIN_LOAD_BYTE, >> + BPF_BUILTIN_LOAD_HALF, >> + BPF_BUILTIN_LOAD_WORD, >> + >> + /* Compile Once - Run Everywhere (CO-RE) support. */ >> + BPF_CORE_BUILTINS_MARKER = 10, >> + BPF_BUILTIN_PRESERVE_ACCESS_INDEX, >> + BPF_BUILTIN_PRESERVE_FIELD_INFO, >> + BPF_BUILTIN_BTF_TYPE_ID, >> + BPF_BUILTIN_PRESERVE_TYPE_INFO, >> + BPF_BUILTIN_PRESERVE_ENUM_VALUE, >> + >> + /* CO-RE INTERNAL reloc. */ >> + BPF_BUILTIN_CORE_RELOC, >> + >> + BPF_BUILTIN_MAX, >> +}; >> + >> +extern GTY (()) tree bpf_builtins[(int) BPF_BUILTIN_MAX]; >> + >> +void bpf_init_core_builtins (void); >> +rtx bpf_expand_core_builtin (tree exp, enum bpf_builtins code); >> +tree bpf_resolve_overloaded_core_builtin (location_t loc, tree fndecl, >> + void *arglist); >> + >> +#endif >> diff --git a/gcc/config/bpf/coreout.cc b/gcc/config/bpf/coreout.cc >> index bd609ad6278f..b84585fb104e 100644 >> --- a/gcc/config/bpf/coreout.cc >> +++ b/gcc/config/bpf/coreout.cc >> @@ -30,6 +30,7 @@ >> #include "ctfc.h" >> #include "btf.h" >> #include "rtl.h" >> +#include "tree-pretty-print.h" >> >> #include "coreout.h" >> >> @@ -146,38 +147,37 @@ static char >> btf_ext_info_section_label[MAX_BTF_EXT_LABEL_BYTES]; >> >> static GTY (()) vec<bpf_core_section_ref, va_gc> *bpf_core_sections; >> >> +struct bpf_core_extra { >> + const char *accessor_str; >> + tree type; >> +}; >> +static hash_map<bpf_core_reloc_ref, struct bpf_core_extra *> >> bpf_comment_info; >> >> /* Create a new BPF CO-RE relocation record, and add it to the appropriate >> CO-RE section. */ >> - >> void >> bpf_core_reloc_add (const tree type, const char * section_name, >> - vec<unsigned int> *accessors, rtx_code_label *label, >> + const char *accessor, >> + rtx_code_label *label, >> enum btf_core_reloc_kind kind) >> { >> - char buf[40]; >> - unsigned int i, n = 0; >> - >> - /* A valid CO-RE access must have at least one accessor. */ >> - if (accessors->length () < 1) >> - return; >> - >> - for (i = 0; i < accessors->length () - 1; i++) >> - n += snprintf (buf + n, sizeof (buf) - n, "%u:", (*accessors)[i]); >> - snprintf (buf + n, sizeof (buf) - n, "%u", (*accessors)[i]); >> - >> bpf_core_reloc_ref bpfcr = ggc_cleared_alloc<bpf_core_reloc_t> (); >> + struct bpf_core_extra *info = ggc_cleared_alloc<struct bpf_core_extra> (); >> ctf_container_ref ctfc = ctf_get_tu_ctfc (); >> >> /* Buffer the access string in the auxiliary strtab. */ >> - ctf_add_string (ctfc, buf, &(bpfcr->bpfcr_astr_off), CTF_AUX_STRTAB); >> - >> + ctf_add_string (ctfc, accessor, &(bpfcr->bpfcr_astr_off), CTF_AUX_STRTAB); >> bpfcr->bpfcr_type = get_btf_id (ctf_lookup_tree_type (ctfc, type)); >> bpfcr->bpfcr_insn_label = label; >> bpfcr->bpfcr_kind = kind; >> >> + info->accessor_str = accessor; >> + info->type = type; >> + bpf_comment_info.put (bpfcr, info); >> + >> /* Add the CO-RE reloc to the appropriate section. */ >> bpf_core_section_ref sec; >> + int i; >> FOR_EACH_VEC_ELT (*bpf_core_sections, i, sec) >> if (strcmp (sec->name, section_name) == 0) >> { >> @@ -288,14 +288,26 @@ output_btfext_header (void) >> static void >> output_asm_btfext_core_reloc (bpf_core_reloc_ref bpfcr) >> { >> + struct bpf_core_extra **info = bpf_comment_info.get (bpfcr); >> + gcc_assert (info != NULL); >> + >> bpfcr->bpfcr_astr_off += ctfc_get_strtab_len (ctf_get_tu_ctfc (), >> CTF_STRTAB); >> >> dw2_assemble_integer (4, gen_rtx_LABEL_REF (Pmode, >> bpfcr->bpfcr_insn_label)); >> - fprintf (asm_out_file, "\t%s bpfcr_insn\n", ASM_COMMENT_START); >> - >> - dw2_asm_output_data (4, bpfcr->bpfcr_type, "bpfcr_type"); >> - dw2_asm_output_data (4, bpfcr->bpfcr_astr_off, "bpfcr_astr_off"); >> + fprintf (asm_out_file, "\t%s%s\n", >> + flag_debug_asm ? ASM_COMMENT_START : "", >> + (flag_debug_asm ? " bpfcr_insn" : "")); >> + >> + /* Extract the pretty print for the type expression. */ >> + pretty_printer pp; >> + dump_generic_node (&pp, (*info)->type, 0, TDF_VOPS|TDF_MEMSYMS|TDF_SLIM, >> + false); >> + char *str = xstrdup (pp_formatted_text (&pp)); >> + >> + dw2_asm_output_data (4, bpfcr->bpfcr_type, "bpfcr_type (%s)", str); >> + dw2_asm_output_data (4, bpfcr->bpfcr_astr_off, "bpfcr_astr_off (\"%s\")", >> + (*info)->accessor_str); >> dw2_asm_output_data (4, bpfcr->bpfcr_kind, "bpfcr_kind"); >> } >> >> diff --git a/gcc/config/bpf/coreout.h b/gcc/config/bpf/coreout.h >> index 8bdb364b7228..c99b1ca885b2 100644 >> --- a/gcc/config/bpf/coreout.h >> +++ b/gcc/config/bpf/coreout.h >> @@ -23,6 +23,7 @@ >> #define __COREOUT_H >> >> #include <stdint.h> >> +#include "ctfc.h" >> >> #ifdef __cplusplus >> extern "C" >> @@ -55,6 +56,7 @@ struct btf_ext_lineinfo >> >> enum btf_core_reloc_kind >> { >> + BPF_RELO_INVALID = -1, >> BPF_RELO_FIELD_BYTE_OFFSET = 0, >> BPF_RELO_FIELD_BYTE_SIZE = 1, >> BPF_RELO_FIELD_EXISTS = 2, >> @@ -66,7 +68,8 @@ enum btf_core_reloc_kind >> BPF_RELO_TYPE_EXISTS = 8, >> BPF_RELO_TYPE_SIZE = 9, >> BPF_RELO_ENUMVAL_EXISTS = 10, >> - BPF_RELO_ENUMVAL_VALUE = 11 >> + BPF_RELO_ENUMVAL_VALUE = 11, >> + BPF_RELO_TYPE_MATCHES = 12 >> }; >> >> struct btf_ext_reloc >> @@ -102,8 +105,12 @@ struct btf_ext_header >> extern void btf_ext_init (void); >> extern void btf_ext_output (void); >> >> -extern void bpf_core_reloc_add (const tree, const char *, vec<unsigned int> >> *, >> - rtx_code_label *, enum btf_core_reloc_kind); >> +void >> +bpf_core_reloc_add (const tree type, const char * section_name, >> + const char *accessor, >> + rtx_code_label *label, >> + enum btf_core_reloc_kind kind); >> + >> extern int bpf_core_get_sou_member_index (ctf_container_ref, const tree); >> >> #ifdef __cplusplus >> diff --git a/gcc/config/bpf/t-bpf b/gcc/config/bpf/t-bpf >> index 3f3cf8daf8fc..c289dde8b173 100644 >> --- a/gcc/config/bpf/t-bpf >> +++ b/gcc/config/bpf/t-bpf >> @@ -1,8 +1,10 @@ >> >> -TM_H += $(srcdir)/config/bpf/coreout.h >> +TM_H += $(srcdir)/config/bpf/coreout.h $(srcdir)/config/bpf/core-builtins.h >> >> coreout.o: $(srcdir)/config/bpf/coreout.cc >> $(COMPILE) $< >> $(POSTCOMPILE) >> >> -PASSES_EXTRA += $(srcdir)/config/bpf/bpf-passes.def >> +core-builtins.o: $(srcdir)/config/bpf/core-builtins.cc >> + $(COMPILE) $< >> + $(POSTCOMPILE) >> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi >> index 97eaacf8a7ec..e06caf38e467 100644 >> --- a/gcc/doc/extend.texi >> +++ b/gcc/doc/extend.texi >> @@ -16015,6 +16015,57 @@ read_y (struct S *arg) >> @end smallexample >> @enddefbuiltin >> >> +@defbuiltin{{unsigned int} __builtin_preserve_enum_value (@var{type}, >> @var{enum}, unsigned int @var{kind})} >> +BPF Compile Once-Run Everywhere (CO-RE) support. This builtin collects enum >> +information and creates a CO-RE relocation relative to @var{enum} that >> should >> +be of @var{type}. The @var{kind} specifies the action performed. >> + >> +The following values are supported for @var{kind}: >> +@table @code >> +@item ENUM_VALUE_EXISTS = 0 >> +The return value is either 0 or 1 depending if the enum value exists in the >> +target. >> + >> +@item ENUM_VALUE = 1 >> +The return value is the enum value in the target kernel. >> +@end table >> +@enddefbuiltin >> + >> +@defbuiltin{{unsigned int} __builtin_btf_type_id (@var{type}, unsigned int >> @var{kind})} >> +BPF Compile Once-Run Everywhere (CO-RE) support. This builtin is used to get >> +the BTF type ID of a specified type. Depending on the @var{kind} argument, >> it >> +will either return the ID of the local BTF information, or the BTF type ID >> in >> +the target kernel. >> + >> +The following values are supported for @var{kind}: >> +@table @code >> +@item BTF_TYPE_ID_LOCAL = 0 >> +Return the local BTF type ID. Always succeeds. >> + >> +@item BTF_TYPE_ID_TARGET = 1 >> +Return the target BTF type ID. If type does not exist in the target, >> returns 0. >> +@end table >> +@enddefbuiltin >> + >> +@defbuiltin{{unsigned int} __builtin_preserve_type_info (@var{type}, >> unsigned int @var{kind})} >> +BPF Compile Once-Run Everywhere (CO-RE) support. This builtin performs named >> +type (struct/union/enum/typedef) verifications. The type of verification >> +dependents on the @var{kind} argument provided. This builtin will always >> +return 0 if type does not exists in the target kernel. >> + >> +The following values are supported for @var{kind}: >> +@table @code >> +@item BTF_TYPE_EXISTS = 0 >> +Checks if type exists in the target. >> + >> +@item BTF_TYPE_MATCHES = 1 >> +Checks if type matches the local definition in the target kernel. >> + >> +@item BTF_TYPE_SIZE = 2 >> +Returns the size of the type within the target. >> +@end table >> +@enddefbuiltin >> + >> @node FR-V Built-in Functions >> @subsection FR-V Built-in Functions