On 7/2/26 3:21 PM, Jose E. Marchesi wrote:
static bool
-bpf_rtx_costs (rtx x ATTRIBUTE_UNUSED,
+bpf_rtx_costs (rtx x,
enum machine_mode mode ATTRIBUTE_UNUSED,
- int outer_code ATTRIBUTE_UNUSED,
+ int outer_code,
int opno ATTRIBUTE_UNUSED,
- int *total ATTRIBUTE_UNUSED,
+ int *total,
bool speed ATTRIBUTE_UNUSED)
{
- /* To be written. */
+ switch (GET_CODE (x))
+ {
+ case CONST_INT:
+ {
+ HOST_WIDE_INT val = INTVAL (x);
+ /* BPF ALU instructions take a signed 32-bit immediate operand. */
+ bool imm32 = (val == (HOST_WIDE_INT) (int32_t) val);
I would prefer if you would define a BPF_IMM32 macro in bpf.h and then
use it as well in the imm32_operand predicate in predicates.md.
OK.
+ switch (outer_code)
+ {
+ /* Do not report a free constant when it is the operand of a
+ multiply, divide or modulo. A zero-cost constant misleads
+ synth_mult () and expand_divmod () into implementing the
+ operation as a sequence of shifts/adds (or a magic-number
+ multiply) instead of BPF's native single-instruction mul/div,
+ which is cheaper here. */
+ case MULT:
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ *total = COSTS_N_INSNS (1);
+ break;
I am confused. If I am reading rtlanal.cc:rtx_cost properly, the value
stored here in *total will be added to the operation's cost, which is
now 1.
rtx_cost() is called for the operation and then for the operands and
recursively so on.
This code is handling CONST_INT operands for MPY/DIV/MOD operation.
And it is saying it has some cost (and not 0 which is for default case
or rest of insns).
And these operations default cost in rtx_cost () is NOT 0, but
COST_IN_INSNS (5) and COST_IN_INSNS (7) respectively for MULT and DIV/MOD.
This means the total cost calculated for mult,div,etc rtx that
operate on imm32 operands will be > 1, which would be bigger than the 1
they would get when they operate on registers (register rtx get cost 0
by default in rtx_cost). Is that what you want?
No because it is not 0 + 1, but {5,7}+1
+
+ default:
+ /* An immediate operand is free; a wider constant needs an
+ extra LD_IMM64. */
+ *total = imm32 ? 0 : COSTS_N_INSNS (1);
Ok this makes instructions with imm32 operands as fast as costly as
instructions operating on source register.
Right this is the crux of the change I was aiming for originally: for x
being CONST_INT just return cost 0 insns, that fixed my use case but
regressed the mpy/div/mod tests.
Thx,
-Vineet
+ }
+
+ return true;
+ }
+
+ case MULT:
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ /* BPF implements these as a single instruction, so keep the native
+ operation cheaper than any synthesized shift/add or magic-multiply
+ sequence. This only influences the choice between available
+ alternatives; where the operation has no insn (e.g. a 64-bit signed
+ divide before -mcpu=v4) expand_divmod () still falls back to a
+ libcall. Return false so the operand costs are still added. */
+ *total = COSTS_N_INSNS (1);
+ return false;
+
+ default:
+ return false;
+ }
return false;
}
diff --git a/gcc/testsuite/gcc.target/bpf/const-cost-model-2.c b/gcc/testsuite/gcc.target/bpf/const-cost-model-2.c
new file mode 100644
index 000000000000..ab37de27b566
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/const-cost-model-2.c
@@ -0,0 +1,36 @@
+/* Verift that function return values are const 0 and 1 and not reg.
+ Extracted from BPF selftest sockopt_multi.c */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -mcpu=v4" } */
+
+struct bpf_sockopt {
+ int level, optname, optlen, retval;
+ void *optval_end, *optval;
+};
+
+typedef unsigned char u8;
+
+int getsockopt_child(struct bpf_sockopt *ctx)
+{
+ u8 *optval_end = ctx->optval_end;
+ u8 *optval = ctx->optval;
+
+// if (ctx->optname != 1)
+// goto out;
+
+ if (ctx->level != 0 || ctx->optname != 1)
+ goto out;
+
+ if (optval[0] != 0x80)
+ return 0;
+
+ ctx->retval = 0;
+ return 1;
+
+out:
+ return 1;
+}
+
+/* { dg-final { scan-assembler-not {\*\(u32 \*\) \(r.\+12\) = r.} } } */
+/* { dg-final { scan-assembler-times {\*\(u32 \*\) \(r.\+12\) = 0} 1 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/const-cost-model.c
b/gcc/testsuite/gcc.target/bpf/const-cost-model.c
new file mode 100644
index 000000000000..8cf2fb584922
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/const-cost-model.c
@@ -0,0 +1,42 @@
+/* Verify that arg const 4 to 2nd call is const and not reg r0 which is
+ ret value of first call.
+ Extracted from BPF selftest attach_probe.c */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -mcpu=v4" } */
+
+extern int bpf_copy_from_user_str(int dst__sz);
+
+_Bool verify_sleepable_user_copy_str(void)
+{
+ int ret;
+ char data_short_pad[4];
+
+ ret = bpf_copy_from_user_str(sizeof(data_short_pad));
+
+ if (ret != 4)
+ return false;
+
+ ret = bpf_copy_from_user_str(sizeof(data_short_pad));
+
+ if (ret != 4)
+ return false;
+
+ return true;
+}
+
+/* Original generated code
+ r1 = 4
+ call bpf_copy_from_user_str
+ r1 = (s32) r0
+ ...
+ call bpf_copy_from_user_str
+
+ Now generated
+ r1 = 4
+ call bpf_copy_from_user_str
+ ...
+ r1 = 4
+ call bpf_copy_from_user_str */
+
+/* { dg-final { scan-assembler-times {r1 = 4} 2 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/divmod-libcall-2.c
b/gcc/testsuite/gcc.target/bpf/divmod-libcall-2.c
index 792d689395a2..c93c084344f1 100644
--- a/gcc/testsuite/gcc.target/bpf/divmod-libcall-2.c
+++ b/gcc/testsuite/gcc.target/bpf/divmod-libcall-2.c
@@ -1,16 +1,23 @@
+/* Counterpart to divmod-libcall-1.c, exercising the path where the
+ __divdi3/__moddi3 libcall is actually used (so the .global decl is
+ emitted). The dividend 'x' is a plain signed 'long' and can be negative,
+ so this is a real signed 64-bit divide: before -mcpu=v4 there is no signed
+ divide insn and no magic-multiply sequence (BPF has no highpart multiply),
+ so the libcall is the only option. */
+
/* { dg-do compile } */
/* { dg-options "-O2 -mcpu=v3" } */
/* { dg-final { scan-assembler "global\t__divdi3" } } */
/* { dg-final { scan-assembler "global\t__moddi3" } } */
-int
-foo (unsigned int len)
+long
+foo (long x)
{
- return ((long)len) * 234 / 5;
+ return x * 234 / 5;
}
-int
-bar (unsigned int len)
+long
+bar (long x)
{
- return ((long)len) * 234 % 5;
+ return x * 234 % 5;
}