Hi everyone,
This patch fixes bit-field accesses in CO-RE code generation.
No regressions. The selftests improved with following diff:
3993c3993
< #463 tc_change_tail:FAIL
---
> #463 tc_change_tail:OK
4013c4013
< #473/4 tc_tunnel/sit_none:FAIL
---
> #473/4 tc_tunnel/sit_none:OK
4028c4028
< #473 tc_tunnel:FAIL
---
> #473 tc_tunnel:OK
Looking forward to your review.
Cheers,
Cupertino
------
This patch corrects GCC to do proper bit shifting for bit-field access
expressions that get patched for CO-RE access.
Since we convert field accesses in an early pass, we needed to
explicitly generate shifting and masking to properly access the
bit-fields.
gcc/ChangeLog:
PR target/123894
* config/bpf/core-builtins.cc
(core_expr_with_field_expr_plus_base): Add parameter to return
the tree node for the type associated with the access mode.
(gen_mask_for_size): New function
(gen_rhs_bitfield_adjust): Add function to adapt bitfield accesses
through shifting and masking.
(gen_lrhs_bitfield_adjust): Add function to adapt bitfields in rhs
when a core expression is used in a lhs assignment.
(requires_bitfield_adjust): Add function to verify if an
expression requires bitfield adaptation.
(replace_core_access_index_comp_expr): Add logic for bitfield
adaptation.
(core_make_builtins): Refactor gimplification. Do bitfield
adjusting.
gcc/testsuite/
* gcc.target/bpf/core-bitfields.c: Added new test.
---
gcc/config/bpf/core-builtins.cc | 238 +++++++++++++++---
gcc/testsuite/gcc.target/bpf/core-bitfields.c | 57 +++++
2 files changed, 261 insertions(+), 34 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/bpf/core-bitfields.c
diff --git a/gcc/config/bpf/core-builtins.cc b/gcc/config/bpf/core-builtins.cc
index d0c6cfdaf1c0..7b994fd6f10b 100644
--- a/gcc/config/bpf/core-builtins.cc
+++ b/gcc/config/bpf/core-builtins.cc
@@ -1328,11 +1328,23 @@ construct_builtin_core_reloc (location_t loc, tree
fndecl, tree *args,
- base + __builtin_preserve_field_expr (expr, SIZEOF) */
static tree
-core_expr_with_field_expr_plus_base (tree base, tree expr, bool leaf_node)
+core_expr_with_field_expr_plus_base (tree base, tree expr, bool leaf_node,
+ tree *access_type)
{
tree type = TREE_TYPE (expr);
tree args[2];
+ tree mem_access_type = lang_hooks.types.type_for_mode (TYPE_MODE (type),
+ TYPE_UNSIGNED (type));
+
+ /* Only use mode type for integer types, where the value can be a bitfield
+ access. */
+ if (TREE_CODE (type) != INTEGER_TYPE)
+ mem_access_type = type;
+
+ mem_access_type = build_pointer_type (mem_access_type);
+ *access_type = mem_access_type;
+
if (base == expr)
return expr;
else if (TREE_CODE (expr) == ARRAY_REF
@@ -1366,12 +1378,13 @@ core_expr_with_field_expr_plus_base (tree base, tree
expr, bool leaf_node)
fold_build1 (NOP_EXPR, ptr_type_node, base),
offset);
- tmp = fold_build1 (NOP_EXPR, build_pointer_type (type), tmp);
+ tmp = fold_build1 (NOP_EXPR, mem_access_type, tmp);
return tmp;
}
else
{
tree fndecl = bpf_builtins[BPF_BUILTIN_PRESERVE_FIELD_INFO];
+
args[0] = expr;
args[1] = build_int_cst (integer_type_node, BPF_FIELD_BYTE_OFFSET);
tree builtin_call = construct_builtin_core_reloc (UNKNOWN_LOCATION,
@@ -1385,7 +1398,7 @@ core_expr_with_field_expr_plus_base (tree base, tree
expr, bool leaf_node)
tree tmp = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node,
fold_build1 (NOP_EXPR, ptr_type_node, base),
fold_build1 (NOP_EXPR, size_type_node, builtin_call));
- tmp = fold_build1 (NOP_EXPR, build_pointer_type (type), tmp);
+ tmp = fold_build1 (NOP_EXPR, mem_access_type, tmp);
return tmp;
}
}
@@ -1460,8 +1473,10 @@ make_core_safe_access_index (tree expr, bool *changed,
bool entry = true)
/* The remaining is to traverse the field part of the field expression. */
if (mode != VOIDmode && var_off == NULL_TREE)
{
+ tree access_type = NULL_TREE;
*changed = true;
- return core_expr_with_field_expr_plus_base (base, expr, true);
+ return core_expr_with_field_expr_plus_base (base, expr, true,
+ &access_type);
}
else
{
@@ -1496,8 +1511,10 @@ make_core_safe_access_index (tree expr, bool *changed,
bool entry = true)
&& mode != VOIDmode && var_off != NULL_TREE)
|| entry == true)
{
+ tree access_type = NULL_TREE;
*changed = true;
- ret = core_expr_with_field_expr_plus_base (ret, expr, false);
+ ret = core_expr_with_field_expr_plus_base (ret, expr, false,
+ &access_type);
}
return ret;
}
@@ -1507,12 +1524,129 @@ make_core_safe_access_index (tree expr, bool *changed,
bool entry = true)
__builtin_preserve_access_index argument expression from within
bpf_resolve_overloaded_core_builtin. */
+static tree
+gen_mask_for_size (tree type, poly_int64 bitsize)
+{
+ tree mask = build_int_cst (type, 1);
+ mask = fold_build2 (LSHIFT_EXPR, type, mask,
+ build_int_cst (type, bitsize));
+ mask = fold_build2 (MINUS_EXPR, type, mask,
+ build_int_cst (type, 1));
+ return mask;
+}
+
+static tree
+gen_rhs_bitfield_adjust (tree bit_type, tree mem_access_type,
+ tree mem_ref, tree offset, tree sizemask)
+{
+ tree value = mem_ref;
+
+ /* Shift read value to position it in least significant bits. */
+ if (TREE_INT_CST_LOW (offset) != 0)
+ value = fold_build2 (RSHIFT_EXPR, mem_access_type, value, offset);
+
+ value = fold_build2 (BIT_AND_EXPR, mem_access_type, value, sizemask);;
+ /* The casting takes care of the sign extension. */
+ value = fold_build1 (NOP_EXPR, bit_type, value);
+ return value;
+}
+
+static tree
+gen_lrhs_bitfied_adjust (tree mem_access_type, tree mem_ref, tree lhs,
+ tree offset, tree bitmask)
+{
+ /* Mask to clear bits in destination bitfield. */
+ tree clear_mask = fold_build2 (LSHIFT_EXPR, mem_access_type, bitmask,
offset);
+ clear_mask = fold_build1 (BIT_NOT_EXPR, mem_access_type, clear_mask);
+
+ /* Collect the value from the pointer reference. */
+ tree read_value = fold_build2 (BIT_AND_EXPR, mem_access_type, mem_ref,
+ clear_mask);
+
+
+ tree new_value = lhs;
+ if (TREE_CODE (lhs) == NOP_EXPR
+ && TREE_TYPE (TREE_OPERAND (lhs, 0)) == mem_access_type)
+ new_value = TREE_OPERAND (lhs, 0);
+ new_value = fold_build1 (NOP_EXPR, mem_access_type, new_value);
+ new_value = fold_build2 (BIT_AND_EXPR, mem_access_type, new_value, bitmask);
+ new_value = fold_build2 (LSHIFT_EXPR, mem_access_type, new_value, offset);
+
+
+ new_value = fold_build2 (BIT_IOR_EXPR, mem_access_type, read_value,
+ new_value);
+ return new_value;
+}
+
+static bool
+requires_bitfield_adjust (tree node, tree *p_mem_access_type,
+ tree *offset, tree *bitmask)
+{
+ poly_int64 bitsize, bitpos;
+ tree var_off;
+ machine_mode mode;
+ int sign, reverse, vol;
+
+ get_inner_reference (node, &bitsize, &bitpos, &var_off, &mode,
+ &sign, &reverse, &vol);
+
+ tree type = TREE_TYPE (node);
+ tree mem_access_type = lang_hooks.types.type_for_mode (TYPE_MODE (type),
+ TYPE_UNSIGNED (type));
+ *p_mem_access_type = mem_access_type;
+
+ /* Only use mode type for integer types, where the value can be a bitfield
+ access. */
+ if (TREE_CODE (type) != INTEGER_TYPE)
+ return false;
+
+ if (TYPE_MAIN_VARIANT (type) != TYPE_MAIN_VARIANT (mem_access_type))
+ {
+ *offset = fold_build2 (TRUNC_MOD_EXPR, mem_access_type,
+ build_int_cst (mem_access_type, bitpos),
+ TYPE_SIZE (mem_access_type));
+
+ *bitmask = gen_mask_for_size (mem_access_type, bitsize);
+
+ return true;
+ }
+
+ return false;
+}
+
static tree
replace_core_access_index_comp_expr (tree *node, int *walk_subtrees,
- void *data ATTRIBUTE_UNUSED)
+ void *data)
{
bool valid = true;
gcc_assert (*node != NULL_TREE);
+ bool is_lhs = (bool) data;
+ tree mem_access_type, offset, bitmask;
+
+ if (TREE_CODE (*node) == MODIFY_EXPR
+ && requires_bitfield_adjust (TREE_OPERAND (*node, 0), &mem_access_type,
+ &offset, &bitmask))
+ {
+ TREE_VISITED (*node) = 1;
+ walk_tree (&TREE_OPERAND (*node, 0), replace_core_access_index_comp_expr,
+ (void *) true, NULL);
+ walk_tree (&TREE_OPERAND (*node, 1), replace_core_access_index_comp_expr,
+ NULL, NULL);
+
+ TREE_TYPE (*node) = TREE_TYPE (TREE_OPERAND (*node, 0));
+ TREE_OPERAND (*node, 1) = gen_lrhs_bitfied_adjust (mem_access_type,
+ TREE_OPERAND (*node, 0),
+ TREE_OPERAND (*node, 1),
+ offset, bitmask);
+
+ *node = fold_build2 (MODIFY_EXPR,
+ TREE_TYPE (TREE_OPERAND (*node, 0)),
+ TREE_OPERAND (*node, 0),
+ TREE_OPERAND (*node, 1));
+
+ *walk_subtrees = false;
+ return NULL_TREE;
+ }
tree *expr = node;
bool should_indirect = false;
@@ -1525,18 +1659,29 @@ replace_core_access_index_comp_expr (tree *node, int
*walk_subtrees,
if (valid == true && n > 0)
{
bool changed = false;
- tree expr_test = make_core_safe_access_index (*expr, &changed);
- *walk_subtrees = 0;
+ tree expr_test = make_core_safe_access_index (*expr, &changed, true);
+ tree access_type = TREE_TYPE (expr_test);
if (changed == true)
{
if (should_indirect == true)
expr_test = fold_build1 (INDIRECT_REF,
- TREE_TYPE (TREE_TYPE (expr_test)),
+ TREE_TYPE (access_type),
expr_test);
+ if (is_lhs == false
+ && requires_bitfield_adjust (*node, &mem_access_type, &offset,
+ &bitmask))
+ {
+ tree orig_type = TREE_TYPE (*node);
+ expr_test = gen_rhs_bitfield_adjust (orig_type, mem_access_type,
+ expr_test, offset, bitmask);
+ }
+
+
*expr = expr_test;
}
+ *walk_subtrees = 0;
}
return NULL_TREE;
}
@@ -1896,49 +2041,74 @@ core_make_builtins (tree *tp, int *walk_subtrees, void
*data)
expr = TREE_OPERAND (*tp, 0);
}
+ poly_int64 bitsize, bitpos;
+ tree var_off;
+ machine_mode mode;
+ int sign, reverse, vol;
+
+ tree base = get_inner_reference (expr, &bitsize, &bitpos, &var_off, &mode,
+ &sign, &reverse, &vol);
+
if (is_attr_preserve_access (expr)
&& (n = compute_field_expr (expr, NULL, &valid, NULL)) > 0
&& valid == true)
{
- poly_int64 bitsize, bitpos;
- tree var_off;
- machine_mode mode;
- int sign, reverse, vol;
-
- tree base = get_inner_reference (expr, &bitsize, &bitpos, &var_off,
&mode,
- &sign, &reverse, &vol);
-
- tree new_expr = core_expr_with_field_expr_plus_base (base, expr, true);
+ tree access_type = NULL_TREE;
+ tree new_expr = core_expr_with_field_expr_plus_base (base, expr, true,
+ &access_type);
/* Abort convertion if it is just a pointer or a reference to an
attributed type. */
if (new_expr != expr)
{
-
gimple_seq before = NULL;
+ gimple_seq after = NULL;
push_gimplify_context ();
-
gimplify_expr (&new_expr, &before, NULL, is_gimple_val, fb_rvalue);
tree new_expr_type = TREE_TYPE (new_expr);
-
- /* Replace original expression by new_expr. If the type is the same
- tree node, good! If it is a pointer type, we need to dereference
- the type within the pointer to guarantee it is the same. */
- if (TREE_TYPE (new_expr) == TREE_TYPE (*tp)
- || (is_addr_expr
- && TREE_TYPE (new_expr_type) == TREE_TYPE (expr)))
- *tp = fold (new_expr);
- else if (TREE_TYPE (new_expr) == build_pointer_type (TREE_TYPE (*tp)))
- *tp = fold_build2 (MEM_REF, TREE_TYPE (*tp),
- new_expr, build_int_cst (ptr_type_node, 0));
+ tree mem_access_type, offset, bitmask;
+
+ if (requires_bitfield_adjust (*tp , &mem_access_type, &offset,
+ &bitmask))
+ {
+ tree value = build_simple_mem_ref (new_expr);
+ if (wi->is_lhs)
+ {
+ gcc_assert (is_gimple_assign (wi->stmt));
+ tree *rhs = gimple_assign_rhs1_ptr (wi->stmt);
+
+ *rhs = gen_lrhs_bitfied_adjust (mem_access_type,
+ value, *rhs, offset,
+ bitmask);
+ gimplify_expr (rhs, &before, &after, is_gimple_val,
+ fb_rvalue);
+ }
+ else
+ {
+ value = gen_rhs_bitfield_adjust (TREE_TYPE (*tp),
+ mem_access_type,
+ value, offset, bitmask);
+ gimplify_expr (&value, &before, NULL, is_gimple_val,
+ fb_rvalue);
+ }
+ *tp = value;
+ }
else
- gcc_assert (0);
+ {
+ if (TYPE_MAIN_VARIANT (new_expr_type) != TYPE_MAIN_VARIANT (
+ TREE_TYPE (*tp))
+ && !is_addr_expr)
+ new_expr = build_simple_mem_ref (new_expr);
+
+ new_expr = fold (new_expr);
+ *tp = new_expr;
+ }
gsi_insert_seq_before (&(wi->gsi), before, GSI_SAME_STMT);
+ gsi_insert_seq_after (&(wi->gsi), after, GSI_SAME_STMT);
pop_gimplify_context (NULL);
-
- *walk_subtrees = false;
+ *walk_subtrees = 0;
}
}
return NULL_TREE;
diff --git a/gcc/testsuite/gcc.target/bpf/core-bitfields.c
b/gcc/testsuite/gcc.target/bpf/core-bitfields.c
new file mode 100644
index 000000000000..6cbaf1035fb4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-bitfields.c
@@ -0,0 +1,57 @@
+/* Test for BPF CO-RE __attribute__((preserve_access_index)) with accesses on
+ LHS and both LHS and RHS of assignment. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -dA -gbtf -mco-re -masm=normal" } */
+
+struct S {
+ unsigned char a: 2;
+ unsigned char b: 2;
+ unsigned char c: 2;
+ unsigned char d: 2;
+} __attribute__((preserve_access_index));
+
+struct S_no {
+ unsigned char a: 2;
+ unsigned char b: 2;
+ unsigned char c: 2;
+ unsigned char d: 2;
+};
+
+char foo (struct S *s, struct S_no *s_no, unsigned char v)
+{
+ unsigned char z = s->c;
+ s->b = v;
+
+ return z;
+}
+char bar (struct S_no *s_no)
+{
+ unsigned char y = __builtin_preserve_access_index (s_no->d);
+ return y;
+}
+
+/* { 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 "bpfcr_astr_off \\(\"0:1\"\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:2\"\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:3\"\\)" 1 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct S\\)" 2 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct S_no\\)" 1 } } */
+
+/* In foo */
+
+/* { dg-final { scan-assembler-times "rsh32\t%r0,4" 1 } } */
+/* { dg-final { scan-assembler-times "lsh32\t%r3,2" 1 } } */
+/* { dg-final { scan-assembler-times "and32\t%r3,12" 1 } } */
+/* { dg-final { scan-assembler-times "and32\t%r4,-13" 1 } } */
+/* { dg-final { scan-assembler-times "and32\t%r0,3" 1 } } */
+/* { dg-final { scan-assembler-times "or32\t%r4,%r3" 1 } } */
+
+/* In bar */
+
+/* { dg-final { scan-assembler-times "rsh32\t%r0,6" 1 } } */
+
+
--
2.47.3