> 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.
Thanks.
>
>>> + 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.
Yeah, that wasn't my point. See below.
>
>> 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
With your change below, that {5,7} now becomes 1. Thats why I wrote "is
now 1".
So, with your change we have:
cost of MUL on IMM32:
cost of MUL: 1 (value amended by bpf_rtx_costs)
cost of IMM32: 1 (value amended by bpf_rtx_costs)
cost of MUL on REG:
cost of MUL: 1 (value amended by bpf_rtx_costs)
cost of REG: 0 (not amendable by bpf_rtx_costs)
Whereas without your change we had:
cost of MUL on IMM32:
cost of MUL: COSTS_N_INSNS (5) (not amended by bpf_rtx_costs)
cost of IMM32: COSTS_N_INSNS (1) (not amended by bpf_rtx_costs)
cast of MUL on REG:
cost of MUL: COSTS_N_INSNS (5) (not amended by bpf_rtx_costs)
cost of REG: 0 (not amendable by bpf_rtx_costs)
So in both cases the total cost of MUL on REG is less than the cost of
MUL on IMM32, so this does not seem to change the relative cost of
MUL/DIV/etc on imm32 wrt the same operations on register.
Or what am I missing? It is late here and I am probably being more
obtuse than usual, if so please bear with me :)
>
>>> +
>>> + 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;
>>> }