[RFC] bpf: don't synthesize sign_extend for ISA v4
Vineet Gupta
vineet.gupta@linux.dev
Mon Apr 13 22:51:18 GMT 2026
On 4/13/26 11:58 AM, Vineet Gupta wrote:
> Currently the extendsidi2 expander generates a shift left+right to
> materialize a sign_extend. This is not needed for ISA v4 which
> natively supports the sign extending move.
>
> There are various other reasons for fixing this:
>
> - The shifts are throwaway work since Combine subsequently undoes them
> anyways, while diminishing potential opportunities for other optim
> transformations it could have done, given to its own limitations of
> max 3 -> 2 combinations.
>
> - Interim passes scuh as CSE1 can also "see thru" a native sign_extend:DI
> better than the shifts, again opening up more optimization opportunites.
>
> - It also helps slightly with debugging Expand dumps as sign_extend is
> obvious vs. 2 shifts.
>
> The fix itself is easy: just gate the expander on non-availabilty of smov
> which in turn is keyed off of -msmov or -mcpu.
>
> Before After
> ----------------------------------------+---------------------------------------
> (insn 8 7 9 2 (set (reg:DI 19 [ _1 ]) | (insn 8 7 9 2 (set (reg:DI 19 [ _1 ])
> (ashift:DI (subreg:DI (reg:SI 24) 0) | (sign_extend:DI (reg:SI 24)))
> (const_int 32 [0x20]))) | (nil))
> (nil)) |
> (insn 9 8 10 2 (set (reg:DI 19 [ _1 ]) |
> (ashiftrt:DI (reg:DI 19 [ _1 ]) |
> (const_int 32 [0x20]))) |
> (nil)) |
>
> This change is clean running bpf.exp testsuite tests.
>
> The complication comes from a subtle yet very important aspect, which is
> in the old regime, expander unconditionally forced src operand to DImode.
>
> | operands[1] = gen_lowpart (DImode, operands[1])
>
> With the patch, src remains SImode which exposes some latent issues in
> the backend and/or "impedance mismatch [1]" with the kernel verifier and it
> reports following additional fails vs. gcc trunk against kernel bpf-next [2]
>
> | #12 attach_probe:FAIL
So we saw this earlier [1] in a different context and the agreed to
defer since the issue won't show on trunk, needing the in-works ABI
changes. Now the same issue shows with this patch and the analysis/fix
[2] remain applicable. The test extracted from selftest attach_probe is
extern int bpf_copy_from_user_str(int dst__sz);
_Bool verify_sleepable_user_copy_str(void)
{
int ret;
char data_short[4];
char data_short_pad[4];
ret = bpf_copy_from_user_str(sizeof(data_short));
if (ret != 4)
return false;
ret = bpf_copy_from_user_str(sizeof(data_short_pad));
if (ret != 4)
return false;
return true;
}
-O2, -mcpu=v4, RTL at the time of expansion is
(call_insn 7 6 8 2 (set (reg:SI 0 %r0)
(call (mem:DI (symbol_ref:DI ("bpf_copy_from_user_str") [flags 0x41] <function_decl 0x7f1fa2947300 bpf_copy_from_user_str>) [0 bpf_copy_from_user_str S8 A64])
(const_int 0 [0]))) "simple.c":9:8 -1
(expr_list:REG_CALL_DECL (symbol_ref:DI ("bpf_copy_from_user_str") [flags 0x41] <function_decl 0x7f1fa2947300 bpf_copy_from_user_str>)
(nil))
(expr_list:SI (use (reg:SI 1 %r1))
(nil)))
(insn 8 7 9 2 (set (reg:SI 23)
(reg:SI 0 %r0)) "simple.c":9:8 -1
(nil))
(insn 9 8 10 2 (set (reg:SI 24)
(reg:SI 23)) "simple.c":9:8 -1
(nil))
(insn 10 9 11 2 (set (reg/v:DI 20 [ ret ])
(sign_extend:DI (reg:SI 24))) "simple.c":9:8 -1
(nil))
(jump_insn 11 10 12 2 (set (pc)
(if_then_else (ne (subreg/s/u:SI (reg/v:DI 20 [ ret ]) 0)
(const_int 4 [0x4]))
(label_ref:DI 36)
(pc))) "simple.c":11:5 48 {*branch_on_si}
(int_list:REG_BR_PROB 719407025 (nil))
-> 36)
(insn 13 12 14 4 (set (reg:SI 1 %r1)
(const_int 4 [0x4])) "simple.c":14:8 -1
(nil))
(call_insn 14 13 15 4 (set (reg:SI 0 %r0)
(call (mem:DI (symbol_ref:DI ("bpf_copy_from_user_str") [flags 0x41] <function_decl 0x7f1fa2947300 bpf_copy_from_user_str>) [0 bpf_copy_from_user_str S8 A64])
(const_int 0 [0]))) "simple.c":14:8 -1
(expr_list:REG_CALL_DECL (symbol_ref:DI ("bpf_copy_from_user_str") [flags 0x41] <function_decl 0x7f1fa2947300 bpf_copy_from_user_str>)
(nil))
(expr_list:SI (use (reg:SI 1 %r1))
(nil)))
CSE1 transforms insn 13 from moving a const 4 to a reg to moveing a reg
guaranteed to be const 4 by virtue of control flow.
(insn 10 9 11 2 (set (reg/v:DI 20 [ ret ])
(sign_extend:DI (reg:SI 23))) "simple.c":9:8 28 {*extendsidi2}
(expr_list:REG_DEAD (reg:SI 24)
(nil)))
(insn 13 12 14 3 (set (reg:SI 1 %r1)
(subreg/s/u:SI (reg/v:DI 20 [ ret ]) 0)) "simple.c":14:8 35 {*movsi}
(expr_list:REG_EQUAL (const_int 4 [0x4])
(nil)))
fwprop1 does a major cleanup, deleting insn 10,
(call_insn 7 6 8 2 (set (reg:SI 0 %r0)
...
(insn 8 7 11 2 (set (reg:SI 23)
(reg:SI 0 %r0)) "simple.c":9:8 35 {*movsi}
(expr_list:REG_DEAD (reg:SI 0 %r0)
(nil)))
(jump_insn 11 8 12 2 (set (pc)
(if_then_else (ne (reg:SI 23)
(const_int 4 [0x4]))
...
-> 36)
(insn 13 12 14 3 (set (reg:SI 1 %r1)
(reg:SI 23)) "simple.c":14:8 35 {*movsi}
(expr_list:REG_EQUAL (const_int 4 [0x4])
(nil)))
and this carries all the way to the end, where movsi generates rN = rM
cause the verifier to complain.
[1] https://gcc.gnu.org/pipermail/bpf/2026-March/000077.html
[2] https://gcc.gnu.org/pipermail/bpf/2026-April/000116.html
-Vineet
More information about the bpf
mailing list