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/ChangeLog: PR target/107844 PR target/107479 PR target/107480 PR target/107481 * config.gcc: Added core-builtins.cc and .o files. * config/bpf/bpf-passes.def: Removed file. * config/bpf/bpf-protos.h (bpf_add_core_reloc, bpf_replace_core_move_operands): New prototypes. * config/bpf/bpf.cc (enum bpf_builtins, is_attr_preserve_access, maybe_make_core_relo, bpf_core_field_info, bpf_core_compute, bpf_core_get_index, bpf_core_new_decl, bpf_core_walk, bpf_is_valid_preserve_field_info_arg, is_attr_preserve_access, handle_attr_preserve, pass_data_bpf_core_attr, pass_bpf_core_attr): Removed. (def_builtin, bpf_expand_builtin, bpf_resolve_overloaded_builtin): Changed. * config/bpf/bpf.md (define_expand mov<MM:mode>): Changed. (mov_reloc_core<mode>): Added. * config/bpf/core-builtins.cc (struct cr_builtin, enum cr_decision struct cr_local, struct cr_final, struct core_builtin_helpers, enum bpf_plugin_states): Added types. (builtins_data, core_builtin_helpers, core_builtin_type_defs): Added variables. (allocate_builtin_data, get_builtin-data, search_builtin_data, remove_parser_plugin, compare_same_kind, compare_same_ptr_expr, compare_same_ptr_type, is_attr_preserve_access, core_field_info, bpf_core_get_index, compute_field_expr, pack_field_expr_for_access_index, pack_field_expr_for_preserve_field, process_field_expr, pack_enum_value, process_enum_value, pack_type, process_type, bpf_require_core_support, make_core_relo, read_kind, kind_access_index, kind_preserve_field_info, kind_enum_value, kind_type_id, kind_preserve_type_info, get_core_builtin_fndecl_for_type, bpf_handle_plugin_finish_type, bpf_init_core_builtins, construct_builtin_core_reloc, bpf_resolve_overloaded_core_builtin, bpf_expand_core_builtin, bpf_add_core_reloc, bpf_replace_core_move_operands): Added functions. * config/bpf/core-builtins.h (enum bpf_builtins): Added. (bpf_init_core_builtins, bpf_expand_core_builtin, bpf_resolve_overloaded_core_builtin): Added functions. * config/bpf/coreout.cc (struct bpf_core_extra): Added. (bpf_core_reloc_add, output_asm_btfext_core_reloc): Changed. * config/bpf/coreout.h (bpf_core_reloc_add) Changed prototype. * config/bpf/t-bpf: Added core-builtins.o. * doc/extend.texi: Added documentation for new BPF builtins.
This commit is contained in:
parent
9fedc3c010
commit
e0a81559c1
11 changed files with 1594 additions and 804 deletions
|
@ -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}"
|
||||
|
|
|
@ -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);
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
else
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
/* 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;
|
||||
|
|
|
@ -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])
|
||||
|
|
1394
gcc/config/bpf/core-builtins.cc
Normal file
1394
gcc/config/bpf/core-builtins.cc
Normal file
File diff suppressed because it is too large
Load diff
35
gcc/config/bpf/core-builtins.h
Normal file
35
gcc/config/bpf/core-builtins.h
Normal file
|
@ -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
|
|
@ -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);
|
||||
fprintf (asm_out_file, "\t%s%s\n",
|
||||
flag_debug_asm ? ASM_COMMENT_START : "",
|
||||
(flag_debug_asm ? " bpfcr_insn" : ""));
|
||||
|
||||
dw2_asm_output_data (4, bpfcr->bpfcr_type, "bpfcr_type");
|
||||
dw2_asm_output_data (4, bpfcr->bpfcr_astr_off, "bpfcr_astr_off");
|
||||
/* 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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue