combine vs. ext_dce (was Re: [RFC] bpf: don't synthesize sign_extend for ISA v4)
Vineet Gupta
vineet.gupta@linux.dev
Wed Apr 15 17:32:13 GMT 2026
+CC gcc-patches which I missed in the orig posting
On 4/13/26 11:58 AM, Vineet Gupta wrote:
> Currently the bpf 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.
While this might be a valid statement in general, it seems to be
incorrect for some of the sefltest failures reported below.
Combine has super powers :-)
Consider the following test, extracted from #413 sockopt_multi
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;
}
-O2 -mcpu=v4
W/o this patch, BPF extendsidi2 expander synthesizes the 2 shifts, along
with the initial mem access
(insn 8 3 9 2 (set (reg:SI 25 [ ctx_7(D)->optname ])
(mem:SI (plus:DI (reg/v/f:DI 24 [ ctx ])
(const_int 4 [0x4])) [3 ctx_7(D)->optname+0 S4
A32])) {*movsi}
(nil))
(insn 9 8 10 2 (set (reg:DI 19 [ _1 ])
(ashift:DI (subreg:DI (reg:SI 25 [ ctx_7(D)->optname ]) 0)
(const_int 32 [0x20]))) {ashldi3}
(expr_list:REG_DEAD (reg:SI 25 [ ctx_7(D)->optname ])
(nil)))
(insn 10 9 11 2 (set (reg:DI 32 [ _1 ])
(ashiftrt:DI (reg:DI 19 [ _1 ])
(const_int 32 [0x20]))) {ashrdi3}
(expr_list:REG_DEAD (reg:DI 19 [ _1 ])
(nil)))
ext_dce can't figure out the 2 shifts (although it seems it was given
the specific capabilites [1]
This allows combine to weaves its magic transforming the 3 insns into a
single sign_extend
insn_cost 4 for 8: r25:SI=[r24:DI+0x4]
insn_cost 4 for 9: r19:DI=r25:SI#0<<0x20
REG_DEAD r25:SI
insn_cost 4 for 10: r32:DI=r19:DI>>0x20
REG_DEAD r19:DI
allowing combination of insns 9 and 10
modifying insn i3 10: r32:DI=sign_extend(r25:SI)
allowing combination of insns 8 and 10
modifying insn i3 10: r32:DI=sign_extend([r24:DI+0x4])
(insn 10 9 11 2 (set (reg:DI 32 [ _1 ])
(sign_extend:DI (mem:SI (plus:DI (reg/v/f:DI 24 [ ctx ])
(const_int 4 [0x4])) [3 ctx_7(D)->optname+0 S4
A32])))
This eventually generates
r0 = *(s32 *) (r1+4)
if w0 != 1 goto .L4
W/ my patch, expander retains sign_extend and RTL before ext_dce is
(insn 8 3 9 2 (set (reg:SI 25 [ ctx_7(D)->optname ])
(mem:SI (plus:DI (reg/v/f:DI 24 [ ctx ])
(const_int 4 [0x4])) [3 ctx_7(D)->optname+0 S4
A32])) {*movsi}
(nil))
(insn 9 8 10 2 (set (reg:DI 19 [ _1 ])
(sign_extend:DI (reg:SI 25 [ ctx_7(D)->optname ])))
{*extendsidi2}
(nil))
ext_dce then does what it is supposed to [2], i.e. replace the
sign_extend with a subreg, but unable to fold the first insn into it.
(insn 8 3 9 2 (set (reg:SI 25 [ ctx_7(D)->optname ])
(mem:SI (plus:DI (reg/v/f:DI 24 [ ctx ])
(const_int 4 [0x4])) [3 ctx_7(D)->optname+0 S4
A32])) {*movsi}
(nil))
(insn 9 8 10 2 (set (reg:DI 19 [ _1 ])
(subreg:DI (reg:SI 25 [ ctx_7(D)->optname ]) 0)) {*movdi}
(nil))
This carries all the way to the end, resulting in
r2 = *(u32 *) (r1+4)
r0 = r2
if w2 != 1 goto .L4
While this is correct code, its not really ideal for the consumer of
this code, the kernel verifier. Its "decompilaton" analysis can't
establish that r0 and r2 are equivalent, likely due to mixed usage of
32-bit and 64-bit regs.
The question is what would be plausible ways to get better outcomes from
ext_dce ?
Thx,
-Vineet
[1] https://gcc.gnu.org/pipermail/gcc-patches/2025-November/700490.html
[2] https://gcc.gnu.org/pipermail/gcc-patches/2025-July/688337.html
>
> - 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
> | #74 cgroup_xattr:FAIL
> | #122 file_reader:FAIL
> | #412 sockopt_inherit:FAIL
> | #413 sockopt_multi:FAIL
> | #415 sockopt_sk:FAIL
> | #517 uprobe_multi_test:FAIL
> | #519 usdt:FAIL
> | #649 verif_scale_pyperf600_iter:FAIL
>
> These fallout issues need to be addressed first but as discussed in the
> bpf patchworks call this morning, I'm sending this out as an RFC.
>
> [1] https://devblogs.microsoft.com/oldnewthing/20180123-00/?p=97865
> [2] 2026-02-18 f620af11c27b ("xsk: avoid double checking against rx queue being full")
>
> gcc/ChangeLog:
>
> * config/bpf/bpf.md (define_expand extendsidi2): Only emit shifts
> if !bpf_has_smov.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/bpf/zero-ext.c: Check Expand dumps to ensure
> sign_extend is present and two shifts are not.
>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> ---
> gcc/config/bpf/bpf.md | 12 ++++++++----
> gcc/testsuite/gcc.target/bpf/zero-ext.c | 5 ++++-
> 2 files changed, 12 insertions(+), 5 deletions(-)
>
> diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md
> index a2bceb8998d7..1431bfd225e0 100644
> --- a/gcc/config/bpf/bpf.md
> +++ b/gcc/config/bpf/bpf.md
> @@ -308,16 +308,20 @@
>
> ;; Sign-extending a 32-bit value into a 64-bit value is achieved using
> ;; shifting, with instructions generated by the expand below.
> +;; Only needed for ISA V3 and prior builds.
>
> (define_expand "extendsidi2"
> [(set (match_operand:DI 0 "register_operand")
> (sign_extend:DI (match_operand:SI 1 "register_operand")))]
> ""
> {
> - operands[1] = gen_lowpart (DImode, operands[1]);
> - emit_insn (gen_ashldi3 (operands[0], operands[1], GEN_INT (32)));
> - emit_insn (gen_ashrdi3 (operands[0], operands[0], GEN_INT (32)));
> - DONE;
> + if (!bpf_has_smov)
> + {
> + operands[1] = gen_lowpart (DImode, operands[1]);
> + emit_insn (gen_ashldi3 (operands[0], operands[1], GEN_INT (32)));
> + emit_insn (gen_ashrdi3 (operands[0], operands[0], GEN_INT (32)));
> + DONE;
> + }
> })
>
> ;; ISA V4 introduces sign-extending move and load operations.
> diff --git a/gcc/testsuite/gcc.target/bpf/zero-ext.c b/gcc/testsuite/gcc.target/bpf/zero-ext.c
> index b6ab7dab8bc0..a6c93d0287b5 100644
> --- a/gcc/testsuite/gcc.target/bpf/zero-ext.c
> +++ b/gcc/testsuite/gcc.target/bpf/zero-ext.c
> @@ -1,5 +1,5 @@
> /* { dg-do compile } */
> -/* { dg-options "-O2 -mcpu=v4" } */
> +/* { dg-options "-O2 -mcpu=v4 -fdump-rtl-expand" } */
>
> int bar_int(void);
> short bar_short(void);
> @@ -11,6 +11,9 @@ int foo_int(void) {
>
> /* { dg-final { scan-assembler-not {r0 = r0} } } */
> /* { dg-final { scan-assembler-times {w0 = w0} 1 } } */
> +/* { dg-final { scan-rtl-dump {sign_extend} "expand" } } */
> +/* { dg-final { scan-rtl-dump-not {ashift} "expand" } } */
> +/* { dg-final { scan-rtl-dump-not {ashiftrt} "expand" } } */
>
> int trigger_zext_hidi(void) {
> if (bar_short() != 1) return 0; else return 1;
More information about the bpf
mailing list