aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/config.gcc4
-rw-r--r--gcc/config/bpf/bpf-passes.def20
-rw-r--r--gcc/config/bpf/bpf-protos.h4
-rw-r--r--gcc/config/bpf/bpf.cc806
-rw-r--r--gcc/config/bpf/bpf.md17
-rw-r--r--gcc/config/bpf/core-builtins.cc1394
-rw-r--r--gcc/config/bpf/core-builtins.h35
-rw-r--r--gcc/config/bpf/coreout.cc50
-rw-r--r--gcc/config/bpf/coreout.h13
-rw-r--r--gcc/config/bpf/t-bpf6
-rw-r--r--gcc/doc/extend.texi51
11 files changed, 1595 insertions, 805 deletions
diff --git a/gcc/config.gcc b/gcc/config.gcc
index 7438e0be..415e0e1 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 deeaee9..0000000
--- 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 b484310..fbe0d8a 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 57817cd..4873475 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 (&param, 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 a69a239..e9c00e4 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 0000000..575e63d
--- /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 0000000..15cd3d3
--- /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 bd609ad..b84585f 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 8bdb364..c99b1ca 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 3f3cf8d..c289dde 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 97eaacf..e06caf3 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