bpf: add preserve_field_info builtin
Add BPF __builtin_preserve_field_info. This builtin is used to extract information to facilitate struct and union relocations performed by the BPF loader, especially for bitfields. The builtin has the following signature: unsigned int __builtin_preserve_field_info (EXPR, unsigned int KIND); Where EXPR is an expression accessing a field of a struct or union. Depending on KIND, different information is returned to the program. The supported values for KIND are as follows: enum { FIELD_BYTE_OFFSET = 0, FIELD_BYTE_SIZE, FIELD_EXISTENCE, FIELD_SIGNEDNESS, FIELD_LSHIFT_U64, FIELD_RSHIFT_U64 }; If -mco-re is in effect (explicitly or implicitly specified), a CO-RE relocation is added for the access in EXPR recording the relevant information according to KIND. gcc/ * config/bpf/bpf.cc: Support __builtin_preserve_field_info. (enum bpf_builtins): Add new builtin. (bpf_init_builtins): Likewise. (bpf_core_field_info): New function. (bpf_expand_builtin): Accomodate new builtin. Refactor adding new relocation to... (maybe_make_core_relo): ... here. New function. (bpf_resolve_overloaded_builtin): Accomodate new builtin. (bpf_core_newdecl): Likewise. (bpf_core_walk): Likewise. (bpf_core_is_maybe_aggregate_access): Improve logic. (struct core_walk_data): New. * config/bpf/coreout.cc (bpf_core_reloc_add): Allow adding different relocation kinds. * config/bpf/coreout.h: Analogous change. * doc/extend.texi: Document BPF __builtin_preserve_field_info. gcc/testsuite/ * gcc.target/bpf/core-builtin-fieldinfo-errors-1.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-errors-2.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-existence-1.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-lshift-1-be.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-lshift-1-le.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-lshift-2.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-offset-1.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-rshift-1.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-rshift-2.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-sign-1.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-sign-2.c: New test. * gcc.target/bpf/core-builtin-fieldinfo-size-1.c: New test.
This commit is contained in:
parent
d2249cd9ad
commit
068baae186
16 changed files with 851 additions and 76 deletions
|
@ -184,13 +184,13 @@ enum bpf_builtins
|
|||
|
||||
/* 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];
|
||||
|
||||
|
||||
void bpf_register_coreattr_pass (void);
|
||||
|
||||
/* Initialize the per-function machine status. */
|
||||
|
@ -966,6 +966,9 @@ bpf_init_builtins (void)
|
|||
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));
|
||||
}
|
||||
|
||||
#undef TARGET_INIT_BUILTINS
|
||||
|
@ -975,6 +978,199 @@ 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. */
|
||||
|
||||
|
@ -1025,17 +1221,15 @@ 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 == -1)
|
||||
{
|
||||
/* A resolved overloaded builtin, e.g. __bpf_preserve_access_index_si */
|
||||
/* A resolved overloaded __builtin_preserve_access_index. */
|
||||
tree arg = CALL_EXPR_ARG (exp, 0);
|
||||
|
||||
if (arg == NULL_TREE)
|
||||
return NULL_RTX;
|
||||
|
||||
auto_vec<unsigned int, 16> accessors;
|
||||
tree container;
|
||||
|
||||
if (TREE_CODE (arg) == SSA_NAME)
|
||||
{
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
|
||||
|
@ -1049,51 +1243,42 @@ bpf_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
|
|||
/* 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))
|
||||
return expand_normal (arg);
|
||||
|
||||
container = bpf_core_compute (arg, &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 expand_normal (arg);
|
||||
|
||||
accessors.reverse ();
|
||||
|
||||
container = TREE_TYPE (container);
|
||||
|
||||
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 (container, section_name, &accessors, label);
|
||||
if (!is_attr_preserve_access (arg))
|
||||
maybe_make_core_relo (arg, BPF_RELO_FIELD_BYTE_OFFSET);
|
||||
|
||||
return expand_normal (arg);
|
||||
}
|
||||
|
||||
else if (code == -2)
|
||||
{
|
||||
/* 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;
|
||||
if (tree_fits_uhwi_p (kind_tree))
|
||||
kind_val = tree_to_uhwi (kind_tree);
|
||||
else
|
||||
error ("invalid argument to built-in function");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
|
@ -1259,41 +1444,64 @@ bpf_core_get_index (const tree node)
|
|||
__builtin_preserve_access_index. */
|
||||
|
||||
static tree
|
||||
bpf_core_newdecl (tree type)
|
||||
bpf_core_newdecl (tree type, bool is_pai)
|
||||
{
|
||||
tree rettype = build_function_type_list (type, type, NULL);
|
||||
tree rettype;
|
||||
char name[80];
|
||||
int len = snprintf (name, sizeof (name), "%s", "__builtin_pai_");
|
||||
static unsigned long pai_count = 0;
|
||||
static unsigned long pfi_count = 0;
|
||||
|
||||
static unsigned long cnt = 0;
|
||||
len = snprintf (name + len, sizeof (name) - len, "%lu", cnt++);
|
||||
if (is_pai)
|
||||
{
|
||||
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++);
|
||||
}
|
||||
else
|
||||
{
|
||||
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++);
|
||||
}
|
||||
|
||||
return add_builtin_function_ext_scope (name, rettype, -1, BUILT_IN_MD, NULL,
|
||||
NULL_TREE);
|
||||
return add_builtin_function_ext_scope (name, rettype, is_pai ? -1 : -2,
|
||||
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 int
|
||||
static bool
|
||||
bpf_core_is_maybe_aggregate_access (tree expr)
|
||||
{
|
||||
enum tree_code code = TREE_CODE (expr);
|
||||
if (code == COMPONENT_REF || code == ARRAY_REF)
|
||||
return 1;
|
||||
|
||||
if (code == ADDR_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));
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct core_walk_data {
|
||||
location_t loc;
|
||||
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)
|
||||
{
|
||||
location_t loc = *((location_t *) data);
|
||||
struct core_walk_data *dat = (struct core_walk_data *) data;
|
||||
bool is_pai = dat->arg == NULL_TREE;
|
||||
|
||||
/* If this is a type, don't do anything. */
|
||||
if (TYPE_P (*tp))
|
||||
|
@ -1302,10 +1510,18 @@ bpf_core_walk (tree *tp, int *walk_subtrees, void *data)
|
|||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Build a new function call to a resolved builtin for the desired operation.
|
||||
If this is a preserve_field_info call, pass along the argument to the
|
||||
resolved builtin call. */
|
||||
if (bpf_core_is_maybe_aggregate_access (*tp))
|
||||
{
|
||||
tree newdecl = bpf_core_newdecl (TREE_TYPE (*tp));
|
||||
tree newcall = build_call_expr_loc (loc, newdecl, 1, *tp);
|
||||
tree newdecl = bpf_core_newdecl (TREE_TYPE (*tp), is_pai);
|
||||
tree newcall;
|
||||
if (is_pai)
|
||||
newcall = build_call_expr_loc (dat->loc, newdecl, 1, *tp);
|
||||
else
|
||||
newcall = build_call_expr_loc (dat->loc, newdecl, 2, *tp, dat->arg);
|
||||
|
||||
*tp = newcall;
|
||||
*walk_subtrees = 0;
|
||||
}
|
||||
|
@ -1330,6 +1546,30 @@ 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.).
|
||||
We use this for the __builtin_preserve_access_index builtin for CO-RE
|
||||
|
@ -1344,7 +1584,12 @@ bpf_small_register_classes_for_mode_p (machine_mode mode)
|
|||
static tree
|
||||
bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
|
||||
{
|
||||
if (DECL_MD_FUNCTION_CODE (fndecl) != BPF_BUILTIN_PRESERVE_ACCESS_INDEX)
|
||||
bool is_pai = DECL_MD_FUNCTION_CODE (fndecl)
|
||||
== BPF_BUILTIN_PRESERVE_ACCESS_INDEX;
|
||||
bool is_pfi = DECL_MD_FUNCTION_CODE (fndecl)
|
||||
== BPF_BUILTIN_PRESERVE_FIELD_INFO;
|
||||
|
||||
if (!is_pai && !is_pfi)
|
||||
return NULL_TREE;
|
||||
|
||||
/* We only expect one argument, but it may be an arbitrarily-complicated
|
||||
|
@ -1352,18 +1597,26 @@ bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
|
|||
vec<tree, va_gc> *params = static_cast<vec<tree, va_gc> *> (arglist);
|
||||
unsigned n_params = params ? params->length() : 0;
|
||||
|
||||
if (n_params != 1)
|
||||
if ((is_pai && n_params != 1) || (is_pfi && n_params != 2))
|
||||
{
|
||||
error_at (loc, "expected exactly 1 argument");
|
||||
return NULL_TREE;
|
||||
error_at (loc, "wrong number of arguments");
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
tree param = (*params)[0];
|
||||
|
||||
/* If not generating BPF_CORE information, the builtin does nothing. */
|
||||
if (!TARGET_BPF_CORE)
|
||||
/* If not generating BPF_CORE information, preserve_access_index does nothing,
|
||||
and simply "resolves to" the argument. */
|
||||
if (!TARGET_BPF_CORE && is_pai)
|
||||
return param;
|
||||
|
||||
if (is_pfi && !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? */
|
||||
|
@ -1387,7 +1640,11 @@ bpf_resolve_overloaded_builtin (location_t loc, tree fndecl, void *arglist)
|
|||
This ensures that all the relevant information remains within the
|
||||
expression trees the builtin finally gets. */
|
||||
|
||||
walk_tree (¶m, bpf_core_walk, (void *) &loc, NULL);
|
||||
struct core_walk_data data;
|
||||
data.loc = loc;
|
||||
data.arg = is_pai ? NULL_TREE : (*params)[1];
|
||||
|
||||
walk_tree (¶m, bpf_core_walk, (void *) &data, NULL);
|
||||
|
||||
return param;
|
||||
}
|
||||
|
@ -1524,7 +1781,8 @@ handle_attr_preserve (function *fn)
|
|||
emit_label (label);
|
||||
|
||||
/* Add the CO-RE relocation information to the BTF container. */
|
||||
bpf_core_reloc_add (container, section_name, &accessors, label);
|
||||
bpf_core_reloc_add (container, section_name, &accessors, label,
|
||||
BPF_RELO_FIELD_BYTE_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,7 +152,8 @@ static GTY (()) vec<bpf_core_section_ref, va_gc> *bpf_core_sections;
|
|||
|
||||
void
|
||||
bpf_core_reloc_add (const tree type, const char * section_name,
|
||||
vec<unsigned int> *accessors, rtx_code_label *label)
|
||||
vec<unsigned int> *accessors, rtx_code_label *label,
|
||||
enum btf_core_reloc_kind kind)
|
||||
{
|
||||
char buf[40];
|
||||
unsigned int i, n = 0;
|
||||
|
@ -173,7 +174,7 @@ bpf_core_reloc_add (const tree type, const char * section_name,
|
|||
|
||||
bpfcr->bpfcr_type = get_btf_id (ctf_lookup_tree_type (ctfc, type));
|
||||
bpfcr->bpfcr_insn_label = label;
|
||||
bpfcr->bpfcr_kind = BPF_RELO_FIELD_BYTE_OFFSET;
|
||||
bpfcr->bpfcr_kind = kind;
|
||||
|
||||
/* Add the CO-RE reloc to the appropriate section. */
|
||||
bpf_core_section_ref sec;
|
||||
|
|
|
@ -103,7 +103,7 @@ 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 *);
|
||||
rtx_code_label *, enum btf_core_reloc_kind);
|
||||
extern int bpf_core_get_sou_member_index (ctf_container_ref, const tree);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -15755,6 +15755,83 @@ Load 32-bits from the @code{struct sk_buff} packet data pointed by the register
|
|||
BPF Compile Once-Run Everywhere (CO-RE) support. Instruct GCC to generate CO-RE relocation records for any accesses to aggregate data structures (struct, union, array types) in @var{expr}. This builtin is otherwise transparent, the return value is whatever @var{expr} evaluates to. It is also overloaded: @var{expr} may be of any type (not necessarily a pointer), the return type is the same. Has no effect if @code{-mco-re} is not in effect (either specified or implied).
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {Built-in Function} unsigned int __builtin_preserve_field_info (@var{expr}, unsigned int @var{kind})
|
||||
BPF Compile Once-Run Everywhere (CO-RE) support. This builtin is used to
|
||||
extract information to aid in struct/union relocations. @var{expr} is
|
||||
an access to a field of a struct or union. Depending on @var{kind}, different
|
||||
information is returned to the program. A CO-RE relocation for the access in
|
||||
@var{expr} with kind @var{kind} is recorded if @code{-mco-re} is in effect.
|
||||
|
||||
The following values are supported for @var{kind}:
|
||||
@table @var
|
||||
@item FIELD_BYTE_OFFSET = 0
|
||||
The returned value is the offset, in bytes, of the field from the
|
||||
beginning of the containing structure. For bitfields, the byte offset
|
||||
of the containing word.
|
||||
|
||||
@item FIELD_BYTE_SIZE = 1
|
||||
The returned value is the size, in bytes, of the field. For bitfields,
|
||||
the size in bytes of the containing word.
|
||||
|
||||
@item FIELD_EXISTENCE = 2
|
||||
The returned value is 1 if the field exists, 0 otherwise. Always 1 at
|
||||
compile time.
|
||||
|
||||
@item FIELD_SIGNEDNESS = 3
|
||||
The returned value is 1 if the field is signed, 0 otherwise.
|
||||
|
||||
@item FIELD_LSHIFT_U64 = 4
|
||||
@itemx FIELD_RSHIFT_U64 = 5
|
||||
The returned value is the number of bits of left- or right-shifting
|
||||
respectively needed in order to recover the original value of the field,
|
||||
after it has been loaded by a read of FIELD_BYTE_SIZE bytes into an
|
||||
unsigned 64-bit value. Primarily useful for reading bitfield values
|
||||
from structures which may change between kernel versions.
|
||||
|
||||
@end table
|
||||
|
||||
Note that the return value is a constant which is known at
|
||||
compile-time. If the field has a variable offset then
|
||||
FIELD_BYTE_OFFSET, FIELD_LSHIFT_U64 and FIELD_RSHIFT_U64 are not
|
||||
supported. Similarly, if the field has a variable size then
|
||||
FIELD_BYTE_SIZE, FIELD_LSHIFT_U64 and FIELD_RSHIFT_U64 are not
|
||||
supported.
|
||||
|
||||
For example, __builtin_preserve_field_info can be used to reliably
|
||||
extract bitfield values from a structure which may change between
|
||||
kernel versions:
|
||||
|
||||
@example
|
||||
struct S
|
||||
@{
|
||||
short a;
|
||||
int x:7;
|
||||
int y:5;
|
||||
@};
|
||||
|
||||
int
|
||||
read_y (struct S *arg)
|
||||
@{
|
||||
unsigned long long val;
|
||||
unsigned int offset = __builtin_preserve_field_info (arg->y, FIELD_BYTE_OFFSET);
|
||||
unsigned int size = __builtin_presrve_field_info (arg->y, FIELD_BYTE_SIZE);
|
||||
|
||||
/* Read size bytes from arg + offset into val. */
|
||||
bpf_probe_read (&val, size, arg + offset);
|
||||
|
||||
val <<= __builtin_preserve_field_info (arg->y, FIELD_LSHIFT_U64);
|
||||
|
||||
if (__builtin_preserve_field_info (arg->y, FIELD_SIGNEDNESS))
|
||||
val = ((long long) val >> __builtin_preserve_field_info (arg->y, FIELD_RSHIFT_U64));
|
||||
else
|
||||
val >>= __builtin_preserve_field_info (arg->y, FIELD_RSHIFT_U64);
|
||||
|
||||
return val;
|
||||
@}
|
||||
|
||||
@end example
|
||||
@end deftypefn
|
||||
|
||||
@node FR-V Built-in Functions
|
||||
@subsection FR-V Built-in Functions
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
struct F {
|
||||
int bar;
|
||||
char c;
|
||||
int baz;
|
||||
int arr[];
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_BYTE_OFFSET = 0,
|
||||
FIELD_BYTE_SIZE = 1,
|
||||
};
|
||||
|
||||
unsigned int test (struct F *f) {
|
||||
|
||||
unsigned x = __builtin_preserve_field_info (f->arr, FIELD_BYTE_SIZE); /* { dg-error "unsupported variable size field access" } */
|
||||
|
||||
unsigned y = __builtin_preserve_field_info (f->baz, 99); /* { dg-error "invalid second argument to built-in function" } */
|
||||
|
||||
return x + y;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
struct F {
|
||||
int bar;
|
||||
char c;
|
||||
int baz;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_BYTE_OFFSET = 0,
|
||||
FIELD_BYTE_SIZE = 1,
|
||||
};
|
||||
|
||||
int test (struct F *f) {
|
||||
int a;
|
||||
unsigned x = __builtin_preserve_field_info (({ a = f->bar + f->baz; }), FIELD_BYTE_OFFSET); /* { dg-error "argument is not a field access" } */
|
||||
|
||||
int b;
|
||||
unsigned y = __builtin_preserve_field_info (&(f->c), FIELD_BYTE_SIZE); /* { dg-error "argument is not a field access" } */
|
||||
|
||||
return a + b + x + y;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
enum {
|
||||
FIELD_EXISTENCE = 2,
|
||||
};
|
||||
|
||||
typedef unsigned uint;
|
||||
|
||||
struct S {
|
||||
unsigned char c;
|
||||
int d;
|
||||
uint u;
|
||||
short ar[3];
|
||||
};
|
||||
|
||||
unsigned int foo (struct S *s)
|
||||
{
|
||||
unsigned c = __builtin_preserve_field_info (s->c, FIELD_EXISTENCE);
|
||||
unsigned d = __builtin_preserve_field_info (s->d, FIELD_EXISTENCE);
|
||||
unsigned u = __builtin_preserve_field_info (s->u, FIELD_EXISTENCE);
|
||||
unsigned ar = __builtin_preserve_field_info (s->ar[1], FIELD_EXISTENCE);
|
||||
|
||||
return c + d + u + ar;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],1" 4 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:3:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0x2\[\t \]+\[^\n\]*bpfcr_kind" 4 } } */
|
|
@ -0,0 +1,37 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re -mbig-endian" } */
|
||||
|
||||
struct S {
|
||||
int x1: 6;
|
||||
int x2: 3;
|
||||
int x3: 7;
|
||||
int x4: 16;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_LSHIFT_U64 = 4,
|
||||
};
|
||||
|
||||
unsigned int foo (struct S *s)
|
||||
{
|
||||
/* little endian: x1=58, x2=55, x3=48, x4=32 */
|
||||
/* big endian: x1=32, x2=38, x3=41, x4=48 */
|
||||
unsigned x1 = __builtin_preserve_field_info (s->x1, FIELD_LSHIFT_U64);
|
||||
unsigned x2 = __builtin_preserve_field_info (s->x2, FIELD_LSHIFT_U64);
|
||||
unsigned x3 = __builtin_preserve_field_info (s->x3, FIELD_LSHIFT_U64);
|
||||
unsigned x4 = __builtin_preserve_field_info (s->x4, FIELD_LSHIFT_U64);
|
||||
|
||||
return x1 + x2 + x3 + x4;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],32" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],38" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],41" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],48" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0x4\[\t \]+\[^\n\]*bpfcr_kind" 4 } } */
|
|
@ -0,0 +1,37 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re -mlittle-endian" } */
|
||||
|
||||
struct S {
|
||||
int x1: 6;
|
||||
int x2: 3;
|
||||
int x3: 7;
|
||||
int x4: 16;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_LSHIFT_U64 = 4,
|
||||
};
|
||||
|
||||
unsigned int foo (struct S *s)
|
||||
{
|
||||
/* little endian: x1=58, x2=55, x3=48, x4=32 */
|
||||
/* big endian: x1=32, x2=38, x3=41, x4=48 */
|
||||
unsigned x1 = __builtin_preserve_field_info (s->x1, FIELD_LSHIFT_U64);
|
||||
unsigned x2 = __builtin_preserve_field_info (s->x2, FIELD_LSHIFT_U64);
|
||||
unsigned x3 = __builtin_preserve_field_info (s->x3, FIELD_LSHIFT_U64);
|
||||
unsigned x4 = __builtin_preserve_field_info (s->x4, FIELD_LSHIFT_U64);
|
||||
|
||||
return x1 + x2 + x3 + x4;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],58" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],55" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],48" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],32" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0x4\[\t \]+\[^\n\]*bpfcr_kind" 4 } } */
|
|
@ -0,0 +1,37 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
struct S {
|
||||
char c;
|
||||
short s;
|
||||
int x;
|
||||
};
|
||||
|
||||
union U {
|
||||
struct S s[2];
|
||||
long long ll;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_LSHIFT_U64 = 4,
|
||||
};
|
||||
|
||||
unsigned int foo (union U *u)
|
||||
{
|
||||
/* s0s = 48, s1c = 56, ll = 0; endianness independent. */
|
||||
unsigned s0s = __builtin_preserve_field_info (u->s[0].s, FIELD_LSHIFT_U64);
|
||||
unsigned s1c = __builtin_preserve_field_info (u->s[1].c, FIELD_LSHIFT_U64);
|
||||
unsigned ll = __builtin_preserve_field_info (u->ll, FIELD_LSHIFT_U64);
|
||||
|
||||
return s0s + s1c + ll;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],48" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],56" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],0" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0:0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0x4\[\t \]+\[^\n\]*bpfcr_kind" 3 } } */
|
|
@ -0,0 +1,56 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
struct S {
|
||||
unsigned int a1: 7;
|
||||
unsigned int a2: 4;
|
||||
unsigned int a3: 13;
|
||||
unsigned int a4: 5;
|
||||
int x;
|
||||
};
|
||||
|
||||
struct T {
|
||||
unsigned int y;
|
||||
struct S s[2];
|
||||
char c;
|
||||
char d;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_BYTE_OFFSET = 0,
|
||||
};
|
||||
|
||||
|
||||
unsigned int foo (struct T *t)
|
||||
{
|
||||
unsigned s0a1 = __builtin_preserve_field_info (t->s[0].a1, FIELD_BYTE_OFFSET);
|
||||
unsigned s0a4 = __builtin_preserve_field_info (t->s[0].a4, FIELD_BYTE_OFFSET);
|
||||
unsigned s0x = __builtin_preserve_field_info (t->s[0].x, FIELD_BYTE_OFFSET);
|
||||
|
||||
unsigned s1a1 = __builtin_preserve_field_info (t->s[1].a1, FIELD_BYTE_OFFSET);
|
||||
unsigned s1a4 = __builtin_preserve_field_info (t->s[1].a4, FIELD_BYTE_OFFSET);
|
||||
unsigned s1x = __builtin_preserve_field_info (t->s[1].x, FIELD_BYTE_OFFSET);
|
||||
|
||||
unsigned c = __builtin_preserve_field_info (t->c, FIELD_BYTE_OFFSET);
|
||||
unsigned d = __builtin_preserve_field_info (t->d, FIELD_BYTE_OFFSET);
|
||||
|
||||
return s0a1 + s0a4 + s0x + s1a1 + s1a4 + s1x + c + d;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],4" 2 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],8" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],12" 2 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],16" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],20" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],21" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:0:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:1:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:1:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0\[\t \]+\[^\n\]*bpfcr_kind" 8 } } */
|
|
@ -0,0 +1,36 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
struct S {
|
||||
int x1: 6;
|
||||
int x2: 3;
|
||||
int x3: 7;
|
||||
int x4: 16;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_RSHIFT_U64 = 5,
|
||||
};
|
||||
|
||||
unsigned int foo (struct S *s)
|
||||
{
|
||||
/* x1=58, x2=61, x3=57, x4=48; endianness independent. */
|
||||
unsigned x1 = __builtin_preserve_field_info (s->x1, FIELD_RSHIFT_U64);
|
||||
unsigned x2 = __builtin_preserve_field_info (s->x2, FIELD_RSHIFT_U64);
|
||||
unsigned x3 = __builtin_preserve_field_info (s->x3, FIELD_RSHIFT_U64);
|
||||
unsigned x4 = __builtin_preserve_field_info (s->x4, FIELD_RSHIFT_U64);
|
||||
|
||||
return x1 + x2 + x3 + x4;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],58" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],61" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],57" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],48" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:3.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0x5\[\t \]+\[^\n\]*bpfcr_kind" 4 } } */
|
|
@ -0,0 +1,35 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
struct S {
|
||||
int x;
|
||||
char c;
|
||||
};
|
||||
|
||||
union U {
|
||||
int i;
|
||||
struct S s;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_RSHIFT_U64 = 5,
|
||||
};
|
||||
|
||||
unsigned int foo (union U *u)
|
||||
{
|
||||
/* sx = 32, sc = 56, i = 32; endianness independent. */
|
||||
unsigned sx = __builtin_preserve_field_info (u->s.x, FIELD_RSHIFT_U64);
|
||||
unsigned sc = __builtin_preserve_field_info (u->s.c, FIELD_RSHIFT_U64);
|
||||
unsigned i = __builtin_preserve_field_info (u->i, FIELD_RSHIFT_U64);
|
||||
|
||||
return sx + sc + i;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],32" 2 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],56" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0x5\[\t \]+\[^\n\]*bpfcr_kind" 3 } } */
|
33
gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-1.c
Normal file
33
gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-1.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
enum {
|
||||
FIELD_SIGNEDNESS = 3,
|
||||
};
|
||||
|
||||
typedef unsigned uint;
|
||||
|
||||
struct S {
|
||||
unsigned char c;
|
||||
int d;
|
||||
uint u;
|
||||
short ar[3];
|
||||
};
|
||||
|
||||
unsigned int foo (struct S *s)
|
||||
{
|
||||
unsigned d = __builtin_preserve_field_info (s->d, FIELD_SIGNEDNESS);
|
||||
unsigned u = __builtin_preserve_field_info (s->u, FIELD_SIGNEDNESS);
|
||||
unsigned ar = __builtin_preserve_field_info (s->ar[1], FIELD_SIGNEDNESS);
|
||||
|
||||
return d + u + ar;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],1" 2 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],0" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:3:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0x3\[\t \]+\[^\n\]*bpfcr_kind" 3 } } */
|
45
gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-2.c
Normal file
45
gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-sign-2.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
enum {
|
||||
FIELD_SIGNEDNESS = 3,
|
||||
};
|
||||
|
||||
enum Esig {
|
||||
SA = -1,
|
||||
SB,
|
||||
SC,
|
||||
};
|
||||
|
||||
enum Eun {
|
||||
UA = 0,
|
||||
UB,
|
||||
};
|
||||
|
||||
struct S {
|
||||
enum Esig sig : 3;
|
||||
enum Eun un : 3;
|
||||
};
|
||||
|
||||
union U {
|
||||
int i;
|
||||
struct S s;
|
||||
};
|
||||
|
||||
unsigned int foo (union U *u)
|
||||
{
|
||||
unsigned i = __builtin_preserve_field_info (u->i, FIELD_SIGNEDNESS);
|
||||
unsigned sig = __builtin_preserve_field_info (u->s.sig, FIELD_SIGNEDNESS);
|
||||
unsigned un = __builtin_preserve_field_info (u->s.un, FIELD_SIGNEDNESS);
|
||||
|
||||
return i + sig + un;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],1" 2 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],0" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "3\[\t \]+\[^\n\]*bpfcr_kind" 3 } } */
|
43
gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-size-1.c
Normal file
43
gcc/testsuite/gcc.target/bpf/core-builtin-fieldinfo-size-1.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O0 -dA -gbtf -mco-re" } */
|
||||
|
||||
struct S {
|
||||
unsigned int a1: 7;
|
||||
unsigned int a2: 4;
|
||||
unsigned int a3: 13;
|
||||
unsigned int a4: 5;
|
||||
char carr[5][3];
|
||||
};
|
||||
|
||||
enum {
|
||||
FIELD_BYTE_SIZE = 1,
|
||||
};
|
||||
|
||||
union U {
|
||||
long long l[3];
|
||||
struct S s;
|
||||
};
|
||||
|
||||
unsigned int foo (union U *u)
|
||||
{
|
||||
unsigned ls = __builtin_preserve_field_info (u->l, FIELD_BYTE_SIZE);
|
||||
unsigned s = __builtin_preserve_field_info (u->s, FIELD_BYTE_SIZE);
|
||||
unsigned a2 = __builtin_preserve_field_info (u->s.a2, FIELD_BYTE_SIZE);
|
||||
unsigned a3 = __builtin_preserve_field_info (u->s.a3, FIELD_BYTE_SIZE);
|
||||
unsigned ca = __builtin_preserve_field_info (u->s.carr, FIELD_BYTE_SIZE);
|
||||
|
||||
return ls + s + a2 + a3 + ca;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],24" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],20" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],4" 2 } } */
|
||||
/* { dg-final { scan-assembler-times "\[\t \]mov\[\t \]%r\[0-9\],15" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:0.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:1.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:2.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "ascii \"0:1:4.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */
|
||||
|
||||
/* { dg-final { scan-assembler-times "0x1\[\t \]+\[^\n\]*bpfcr_kind" 5 } } */
|
Loading…
Add table
Reference in a new issue