[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