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?
>
>
next prev parent 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