BPF List
 help / color / mirror / Atom feed
From: Yonghong Song <yonghong.song@linux.dev>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: bpf <bpf@vger.kernel.org>, Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Eduard Zingerman <eddyz87@gmail.com>,
	Kernel Team <kernel-team@fb.com>
Subject: Re: [PATCH bpf-next 07/12] bpf, x86: Place kfunc arguments per the SysV calling convention
Date: Mon, 7 Sep 2026 22:10:04 -0700	[thread overview]
Message-ID: <541c4568-122f-4005-a093-4dfdada439ab@linux.dev> (raw)
In-Reply-To: <7be30b65-0a22-47b6-8635-e0054c6cba24@linux.dev>



On 9/7/26 10:02 PM, Yonghong Song wrote:
>
>
> On 9/7/26 9:33 PM, Alexei Starovoitov wrote:
>> On Sun, Sep 6, 2026 at 1:15 PM Yonghong Song 
>> <yonghong.song@linux.dev> wrote:
>>>
>>>
>>> On 9/4/26 4:58 PM, Alexei Starovoitov wrote:
>>>> On Thu Sep 3, 2026 at 10:10 PM PDT, Yonghong Song wrote:
>>>>> The JIT hands each eightbyte the BPF calling convention passes an
>>>>> argument in to the argument position of the same number, registers 
>>>>> first,
>>>>> so the two conventions agree unless the kernel one places an argument
>>>>> somewhere else. Compute where SysV wants each eightbyte, and move the
>>>>> ones that differ before the call.
>>>>>
>>>>> SysV disagrees over an argument that the registers left cannot 
>>>>> hold: it
>>>>> moves the whole of it to the stack and leaves the registers to the
>>>>> arguments that follow, while the BPF convention splits it and keeps
>>>>> filling slots in order. So for
>>>>>
>>>>>     u64 f(u64 a, u64 b, u64 c, u64 d, u64 e, struct pair s);
>>>>>
>>>>> the BPF convention puts s in the last argument register and the first
>>>>> stack slot, while SysV puts it wholly on the stack. Add an 
>>>>> argument after
>>>>> s and it takes the register s vacated, which makes the moves a 
>>>>> cycle, so
>>>>> one value at a time waits in RAX, dead before a call.
>>>>>
>>>>> The outgoing argument area is sized for both conventions, as SysV 
>>>>> can put
>>>>> on the stack an argument the BPF slots kept in a register, and
>>>>> bpf_jit_supports_kfunc_arg_slot() can now answer yes to any 
>>>>> placement.
>>>> What is SysV ? This change is about x86-64 ABI.
>>> SysV refers to https://wiki.osdev.org/System_V_ABI#x86-64.
>>>
>>>> How come BPF calling convention diverged from x86-64?
>>> Let me explain the calling convention difference between BPF and 
>>> x86_64.
>>>
>>> For x86_64,
>>>      rdi, rsi, rdx, rcx, r8, r9, <stack...>
>>> So for
>>>      foo(long a, __int128 b, __int128 c, __int128 d, __int128 e, 
>>> long f)
>>> the register/stack mapping:
>>>      a:rdi, b:rsi/rdx, c:rcx/r8, d:stack, e:stack, f/r9
>>>
>>> For bpf, the abi is simply sequential, e.g. for the above foo()
>>>      a, b, c, d, e, f // no gap
>>> For bpf, bar(long a, long b, __int128 c, __int128 d, __int128 e, 
>>> long f, __int128 g)
>>>      a, b, c, d, e, f, g // no gap
>>>
>>> The upstream llvm:
>>>
>>> SDValue 
>>> BPFTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
>>> SmallVectorImpl<SDValue> &InVals) const {
>>> ...
>>>     // Walk arg assignments
>>>     for (size_t i = 0; i < OutVals.size(); ++i) {
>>>       CCValAssign &VA = ArgLocs[i];
>>>       SDValue &Arg = OutVals[i];
>>>
>>>       // Promote the value if needed.
>>>       switch (VA.getLocInfo()) {
>>>       default:
>>>         report_fatal_error("unhandled location info: " + 
>>> Twine(VA.getLocInfo()));
>>>       case CCValAssign::Full:
>>>         break;
>>>       case CCValAssign::SExt:
>>>         Arg = DAG.getNode(ISD::SIGN_EXTEND, CLI.DL, VA.getLocVT(), 
>>> Arg);
>>>         break;
>>>       case CCValAssign::ZExt:
>>>         Arg = DAG.getNode(ISD::ZERO_EXTEND, CLI.DL, VA.getLocVT(), 
>>> Arg);
>>>         break;
>>>       case CCValAssign::AExt:
>>>         Arg = DAG.getNode(ISD::ANY_EXTEND, CLI.DL, VA.getLocVT(), Arg);
>>>         break;
>>>       }
>>>
>>>
>>>       // Push arguments into RegsToPass vector
>>>       if (VA.isRegLoc()) {
>>>         RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg));
>>>         continue;
>>>       }
>>>
>>>       if (VA.isMemLoc()) {
>>>         int Off = -8 - VA.getLocMemOffset();
>>>         if (Off < INT16_MIN) {
>>>           fail(CLI.DL, DAG, "extra parameter stack depth exceeded 
>>> limit");
>>>           break;
>>>         }
>>>
>>>         // STORE_STACK_ARG requires i64 operands. With ALU32 mode, 
>>> the CC
>>>         // promotion may only extend to i32, so extend to i64 if 
>>> needed.
>>>         if (Arg.getValueType() != MVT::i64)
>>>           Arg = DAG.getNode(ISD::ANY_EXTEND, CLI.DL, MVT::i64, Arg);
>>>
>>>         SDValue OffVal = DAG.getConstant(Off, CLI.DL, MVT::i64);
>>>         Chain = DAG.getNode(BPFISD::STORE_STACK_ARG, CLI.DL, 
>>> MVT::Other, Chain,
>>>                             OffVal, Arg);
>>>         continue;
>>>       }
>>>
>>>       report_fatal_error("unhandled argument location");
>>>     }
>>>
>>>   From above, for a 16-byte struct e.g. __int128 or struct {long a; 
>>> long b;};
>>> they can have half in R5 and the other half in Stack. But this is not
>>> allowed for x86_64.
>>>
>>> arm64 has more constraints on top of x86_64. For any 16-byte (or > 8 
>>> byte)
>>> argument, the argument must be 16-byte aligned (in registers or in 
>>> stacks).
>>> For the following example:
>>>     foo(long a, __int128 b, __int128 c, __int128 d, __int128 e, long 
>>> f, __int128 g)
>>> the registers/stack:
>>>     a: x0, b: x2/x3, c: x4/x5, d: x6/x7
>>>     stack: e (offset 0), f (offset 16), g (offset 32).
>>>
>>>> Maybe we should adjust bpf side instead.
>>> We can keep llvm implementation but we can adjust bpf side in kernel.
>>>
>>> For x86, if we have a reorder of an argument or a hole in the slots, 
>>> reject.
>>> For arm64, if there is a hole in the slots, reject.
>>>
>>> This will simplify jit a lot.
>> Why cannot we adjust bpf calling convention to match arm64/x86 the best?
>> Since arm64 is stricter, I'd pick that style.
>> There are no kfuncs that use int128 or 16+ byte args,

This patch intends to support kfuncs for int128 or <= 16 bytes argument.

>> so it's a matter of bpf subprogs calling bpf subprogs.

For bpf to bpf call, if I understand correctly, there is no calling convention issue.


>> Seems cleaner to adjust what llvm emits instead of forcing all jits 
>> to adapt.
>
> The following is what I was suggested:
>
> llvm: no change,
>
> x86_64 jit:
>
> +bool bpf_jit_supports_kfunc_arg_slot(u32 slots_used, u32 nslots, u32 
> align)
> +{
> +       if (slots_used >= 6)
> +               return IS_ALIGNED((slots_used - 6) * sizeof(u64), align);
> +
> +       return slots_used + nslots <= 6;
> +}
>
> arm64 jit:
>
> +bool bpf_jit_supports_kfunc_arg_slot(u32 slots_used, u32 nslots, u32 
> align)
> +{
> +       if (slots_used >= 8)
> +               return IS_ALIGNED((slots_used - 8) * sizeof(u64), align);
> +
> +       if (!IS_ALIGNED(slots_used * sizeof(u64), align))
> +               return false;
> +
> +       return slots_used + nslots <= 8;
> +}
>
> The above x86_64 and arm64 will reject for certain cases as in the above.
>
> The alternative solution is to change llvm's. Let us say we want to have
> arm64 calling convention. We may have
>
>    slot 0: int
>    slot 1: empty
>    slot 2: first64 in int128
>    slot 3: second64 in int128
>    slot 4: int
>    slot 5: empty
>    slot 6: first64 in int128
>    slot 7: second64 int int128
>    slot 8: int
>
> in llvm and it matches to arm64
> but it has some issues in kernel as some slot is empty. (slot 1, slot 5).
> and it needs calling convention change for x86_64.
>
> So in llvm, we should have strict arm64 calling convention without holes.
> That means the above slot 0->8 example will be rejected in llvm.
> Is this what you suggested?
>
>


  reply	other threads:[~2026-09-08  5:10 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-04  5:09 [PATCH bpf-next 00/12] bpf: Support by-value struct and __int128 arguments Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 01/12] selftests/bpf: Add a test for an __int128 by-value argument Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 02/12] bpf: Index global function arguments by argument slot Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 03/12] bpf: Support by-value struct arguments up to 16 bytes Yonghong Song
2026-09-04  5:23   ` sashiko-bot
2026-09-08  4:17     ` Yonghong Song
2026-09-04  6:09   ` bot+bpf-ci
2026-09-08  4:19     ` Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 04/12] bpf: Support __int128 as a by-value function argument Yonghong Song
2026-09-04  5:32   ` sashiko-bot
2026-09-08  4:20     ` Yonghong Song
2026-09-04  6:09   ` bot+bpf-ci
2026-09-08  4:21     ` Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 05/12] bpf: Support by-value struct and __int128 kfunc arguments Yonghong Song
2026-09-04  6:18   ` sashiko-bot
2026-09-08  4:22     ` Yonghong Song
2026-09-04  6:24   ` bot+bpf-ci
2026-09-08  4:23     ` Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 06/12] bpf: Add a JIT helper for the outgoing stack of kfunc calls Yonghong Song
2026-09-04  5:25   ` sashiko-bot
2026-09-08  4:26     ` Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 07/12] bpf, x86: Place kfunc arguments per the SysV calling convention Yonghong Song
2026-09-04  5:36   ` sashiko-bot
2026-09-08  4:27     ` Yonghong Song
2026-09-04 23:58   ` Alexei Starovoitov
2026-09-06 20:15     ` Yonghong Song
2026-09-08  4:33       ` Alexei Starovoitov
2026-09-08  5:02         ` Yonghong Song
2026-09-08  5:10           ` Yonghong Song [this message]
2026-09-08 15:24           ` Alexei Starovoitov
2026-09-08 18:43             ` Yonghong Song
2026-09-09  1:59               ` Alexei Starovoitov
2026-09-04  5:10 ` [PATCH bpf-next 08/12] bpf: Record a 16-byte argument alignment in the function model Yonghong Song
2026-09-04  6:09   ` bot+bpf-ci
2026-09-08  4:28     ` Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 09/12] bpf, arm64: Place kfunc arguments per AAPCS64 Yonghong Song
2026-09-04  6:09   ` bot+bpf-ci
2026-09-04  5:10 ` [PATCH bpf-next 10/12] selftests/bpf: Add C tests for by-value arguments up to 16 bytes Yonghong Song
2026-09-04  5:19   ` sashiko-bot
2026-09-08  4:33     ` Yonghong Song
2026-09-04  6:09   ` bot+bpf-ci
2026-09-08  4:34     ` Yonghong Song
2026-09-04  5:10 ` [PATCH bpf-next 11/12] selftests/bpf: Add inline-asm tests for by-value arguments Yonghong Song
2026-09-04  6:09   ` bot+bpf-ci
2026-09-08  4:35     ` Yonghong Song
2026-09-04  5:11 ` [PATCH bpf-next 12/12] selftests/bpf: Add tests for by-value kfunc arguments Yonghong Song

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=541c4568-122f-4005-a093-4dfdada439ab@linux.dev \
    --to=yonghong.song@linux.dev \
    --cc=alexei.starovoitov@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=kernel-team@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox