* [PATCH bpf-next] bpf, sparc: Add support for BPF_SDIV and BPF_SMOD in the sparc64 JIT
@ 2026-07-16 15:11 Nicholas Dudar
2026-07-29 0:33 ` Kuan-Wei Chiu
0 siblings, 1 reply; 3+ messages in thread
From: Nicholas Dudar @ 2026-07-16 15:11 UTC (permalink / raw)
To: ast, daniel, andrii, eddyz87, memxor, davem, andreas
Cc: bpf, sparclinux, linux-kernel, visitorckw
build_insn()'s div/mod emit sites pick DIV/UDIVX by ALU width only,
never by insn->off, so the sparc64 JIT does not implement signed
BPF_SDIV and BPF_SMOD (off == 1). Signed ALU32 and ALU64 div/mod get
an unsigned quotient and remainder rather than the verifier's and the
interpreter's signed result for negative operands.
Emit SDIV/SDIVX on the signed path across the ALU32 and ALU64, X-form
and K-form div/mod sites. The 32-bit signed divide takes a 64-bit
dividend from %y:dst, so sign-fill %y from dst before SDIV where the
unsigned path zeroes it; the 64-bit SDIVX skips the %y write. Route the
signed ALU32 result through do_alu32_trunc, since SDIV does not leave
bits 63:32 clear the way unsigned DIV does.
The verifier and bpf_do_misc_fixups() remove the zero divisor and
INT_MIN / -1 cases before the JIT runs.
Signed-off-by: Nicholas Dudar <main.kalliope@gmail.com>
Assisted-by: Claude:claude-opus-4-8
---
arch/sparc/net/bpf_jit_comp_64.c | 92 +++++++++++++++++++++++++++-----
1 file changed, 80 insertions(+), 12 deletions(-)
diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c
index 2fa0e9375127..6de9fb29944c 100644
--- a/arch/sparc/net/bpf_jit_comp_64.c
+++ b/arch/sparc/net/bpf_jit_comp_64.c
@@ -149,6 +149,8 @@ static u32 WDISP10(u32 off)
#define MULX F3(2, 0x09)
#define UDIVX F3(2, 0x0d)
#define DIV F3(2, 0x0e)
+#define SDIV F3(2, 0x0f)
+#define SDIVX F3(2, 0x2d)
#define SLL F3(2, 0x25)
#define SLLX (F3(2, 0x25)|(1<<12))
#define SRA F3(2, 0x27)
@@ -941,32 +943,69 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
case BPF_ALU64 | BPF_MUL | BPF_X:
emit_alu(MULX, src, dst, ctx);
break;
- case BPF_ALU | BPF_DIV | BPF_X:
+ case BPF_ALU | BPF_DIV | BPF_X: {
+ const bool is_signed = (off == 1);
+
+ if (is_signed) {
+ const u8 tmp = bpf2sparc[TMP_REG_1];
+
+ ctx->tmp_1_used = true;
+
+ /* Sign-extend dst into %y for 32-bit sdiv. */
+ emit_alu3_K(SRA, dst, 31, tmp, ctx);
+ emit_write_y(tmp, ctx);
+ emit_alu(SDIV, src, dst, ctx);
+ /*
+ * SDIV does not guarantee the zero-extension that
+ * the unsigned DIV path relies on, so clear bits
+ * 63:32 through the shared truncation instead of
+ * consuming the verifier's zext marker, as the
+ * other ALU32 ops do.
+ */
+ goto do_alu32_trunc;
+ }
emit_write_y(G0, ctx);
emit_alu(DIV, src, dst, ctx);
if (insn_is_zext(&insn[1]))
return 1;
break;
+ }
case BPF_ALU64 | BPF_DIV | BPF_X:
- emit_alu(UDIVX, src, dst, ctx);
+ if (off == 1)
+ emit_alu(SDIVX, src, dst, ctx);
+ else
+ emit_alu(UDIVX, src, dst, ctx);
break;
case BPF_ALU | BPF_MOD | BPF_X: {
const u8 tmp = bpf2sparc[TMP_REG_1];
+ const bool is_signed = (off == 1);
ctx->tmp_1_used = true;
- emit_write_y(G0, ctx);
- emit_alu3(DIV, dst, src, tmp, ctx);
+ if (is_signed) {
+ const u8 tmp2 = bpf2sparc[TMP_REG_2];
+
+ ctx->tmp_2_used = true;
+
+ /* Sign-extend dst into %y for 32-bit sdiv. */
+ emit_alu3_K(SRA, dst, 31, tmp2, ctx);
+ emit_write_y(tmp2, ctx);
+ emit_alu3(SDIV, dst, src, tmp, ctx);
+ } else {
+ emit_write_y(G0, ctx);
+ emit_alu3(DIV, dst, src, tmp, ctx);
+ }
emit_alu3(MULX, tmp, src, tmp, ctx);
emit_alu3(SUB, dst, tmp, dst, ctx);
goto do_alu32_trunc;
}
case BPF_ALU64 | BPF_MOD | BPF_X: {
const u8 tmp = bpf2sparc[TMP_REG_1];
+ const unsigned int mod = (off == 1) ? SDIVX : UDIVX;
ctx->tmp_1_used = true;
- emit_alu3(UDIVX, dst, src, tmp, ctx);
+ emit_alu3(mod, dst, src, tmp, ctx);
emit_alu3(MULX, tmp, src, tmp, ctx);
emit_alu3(SUB, dst, tmp, dst, ctx);
break;
@@ -1096,33 +1135,62 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
case BPF_ALU64 | BPF_MUL | BPF_K:
emit_alu_K(MULX, dst, imm, ctx);
break;
- case BPF_ALU | BPF_DIV | BPF_K:
+ case BPF_ALU | BPF_DIV | BPF_K: {
+ const bool is_signed = (off == 1);
+
if (imm == 0)
return -EINVAL;
- emit_write_y(G0, ctx);
- emit_alu_K(DIV, dst, imm, ctx);
+ if (is_signed) {
+ const u8 tmp = bpf2sparc[TMP_REG_2];
+
+ ctx->tmp_2_used = true;
+
+ /* Sign-extend dst into %y for 32-bit sdiv. */
+ emit_alu3_K(SRA, dst, 31, tmp, ctx);
+ emit_write_y(tmp, ctx);
+ emit_alu_K(SDIV, dst, imm, ctx);
+ } else {
+ emit_write_y(G0, ctx);
+ emit_alu_K(DIV, dst, imm, ctx);
+ }
goto do_alu32_trunc;
+ }
case BPF_ALU64 | BPF_DIV | BPF_K:
if (imm == 0)
return -EINVAL;
- emit_alu_K(UDIVX, dst, imm, ctx);
+ emit_alu_K((off == 1) ? SDIVX : UDIVX, dst, imm, ctx);
break;
case BPF_ALU64 | BPF_MOD | BPF_K:
case BPF_ALU | BPF_MOD | BPF_K: {
const u8 tmp = bpf2sparc[TMP_REG_2];
+ const bool is_signed = (off == 1);
unsigned int div;
if (imm == 0)
return -EINVAL;
- div = (BPF_CLASS(code) == BPF_ALU64) ? UDIVX : DIV;
+ if (BPF_CLASS(code) == BPF_ALU64)
+ div = is_signed ? SDIVX : UDIVX;
+ else
+ div = is_signed ? SDIV : DIV;
ctx->tmp_2_used = true;
- if (BPF_CLASS(code) != BPF_ALU64)
- emit_write_y(G0, ctx);
+ if (BPF_CLASS(code) != BPF_ALU64) {
+ if (is_signed) {
+ const u8 tmp3 = bpf2sparc[TMP_REG_3];
+
+ ctx->tmp_3_used = true;
+
+ /* Sign-extend dst into %y for 32-bit sdiv. */
+ emit_alu3_K(SRA, dst, 31, tmp3, ctx);
+ emit_write_y(tmp3, ctx);
+ } else {
+ emit_write_y(G0, ctx);
+ }
+ }
if (is_simm13(imm)) {
emit(div | IMMED | RS1(dst) | S13(imm) | RD(tmp), ctx);
emit(MULX | IMMED | RS1(tmp) | S13(imm) | RD(tmp), ctx);
base-commit: d1f4b56417a3dc1a0600f960b14f46bd25eda89d
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH bpf-next] bpf, sparc: Add support for BPF_SDIV and BPF_SMOD in the sparc64 JIT
2026-07-16 15:11 [PATCH bpf-next] bpf, sparc: Add support for BPF_SDIV and BPF_SMOD in the sparc64 JIT Nicholas Dudar
@ 2026-07-29 0:33 ` Kuan-Wei Chiu
2026-07-29 1:36 ` Nicholas Dudar
0 siblings, 1 reply; 3+ messages in thread
From: Kuan-Wei Chiu @ 2026-07-29 0:33 UTC (permalink / raw)
To: Nicholas Dudar
Cc: ast, daniel, andrii, eddyz87, memxor, davem, andreas, bpf,
sparclinux, linux-kernel
On Thu, Jul 16, 2026 at 11:11:10AM -0400, Nicholas Dudar wrote:
> build_insn()'s div/mod emit sites pick DIV/UDIVX by ALU width only,
> never by insn->off, so the sparc64 JIT does not implement signed
> BPF_SDIV and BPF_SMOD (off == 1). Signed ALU32 and ALU64 div/mod get
> an unsigned quotient and remainder rather than the verifier's and the
> interpreter's signed result for negative operands.
>
> Emit SDIV/SDIVX on the signed path across the ALU32 and ALU64, X-form
> and K-form div/mod sites. The 32-bit signed divide takes a 64-bit
> dividend from %y:dst, so sign-fill %y from dst before SDIV where the
> unsigned path zeroes it; the 64-bit SDIVX skips the %y write. Route the
> signed ALU32 result through do_alu32_trunc, since SDIV does not leave
> bits 63:32 clear the way unsigned DIV does.
>
> The verifier and bpf_do_misc_fixups() remove the zero divisor and
> INT_MIN / -1 cases before the JIT runs.
>
> Signed-off-by: Nicholas Dudar <main.kalliope@gmail.com>
> Assisted-by: Claude:claude-opus-4-8
> ---
> arch/sparc/net/bpf_jit_comp_64.c | 92 +++++++++++++++++++++++++++-----
> 1 file changed, 80 insertions(+), 12 deletions(-)
I saw that your bpf jit patches span multiple architectures.
Assuming you don't have access to all these different hardware platforms.
Just out of curiosity how were these patches tested?
Regards,
Kuan-Wei
>
> diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c
> index 2fa0e9375127..6de9fb29944c 100644
> --- a/arch/sparc/net/bpf_jit_comp_64.c
> +++ b/arch/sparc/net/bpf_jit_comp_64.c
> @@ -149,6 +149,8 @@ static u32 WDISP10(u32 off)
> #define MULX F3(2, 0x09)
> #define UDIVX F3(2, 0x0d)
> #define DIV F3(2, 0x0e)
> +#define SDIV F3(2, 0x0f)
> +#define SDIVX F3(2, 0x2d)
> #define SLL F3(2, 0x25)
> #define SLLX (F3(2, 0x25)|(1<<12))
> #define SRA F3(2, 0x27)
> @@ -941,32 +943,69 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
> case BPF_ALU64 | BPF_MUL | BPF_X:
> emit_alu(MULX, src, dst, ctx);
> break;
> - case BPF_ALU | BPF_DIV | BPF_X:
> + case BPF_ALU | BPF_DIV | BPF_X: {
> + const bool is_signed = (off == 1);
> +
> + if (is_signed) {
> + const u8 tmp = bpf2sparc[TMP_REG_1];
> +
> + ctx->tmp_1_used = true;
> +
> + /* Sign-extend dst into %y for 32-bit sdiv. */
> + emit_alu3_K(SRA, dst, 31, tmp, ctx);
> + emit_write_y(tmp, ctx);
> + emit_alu(SDIV, src, dst, ctx);
> + /*
> + * SDIV does not guarantee the zero-extension that
> + * the unsigned DIV path relies on, so clear bits
> + * 63:32 through the shared truncation instead of
> + * consuming the verifier's zext marker, as the
> + * other ALU32 ops do.
> + */
> + goto do_alu32_trunc;
> + }
> emit_write_y(G0, ctx);
> emit_alu(DIV, src, dst, ctx);
> if (insn_is_zext(&insn[1]))
> return 1;
> break;
> + }
> case BPF_ALU64 | BPF_DIV | BPF_X:
> - emit_alu(UDIVX, src, dst, ctx);
> + if (off == 1)
> + emit_alu(SDIVX, src, dst, ctx);
> + else
> + emit_alu(UDIVX, src, dst, ctx);
> break;
> case BPF_ALU | BPF_MOD | BPF_X: {
> const u8 tmp = bpf2sparc[TMP_REG_1];
> + const bool is_signed = (off == 1);
>
> ctx->tmp_1_used = true;
>
> - emit_write_y(G0, ctx);
> - emit_alu3(DIV, dst, src, tmp, ctx);
> + if (is_signed) {
> + const u8 tmp2 = bpf2sparc[TMP_REG_2];
> +
> + ctx->tmp_2_used = true;
> +
> + /* Sign-extend dst into %y for 32-bit sdiv. */
> + emit_alu3_K(SRA, dst, 31, tmp2, ctx);
> + emit_write_y(tmp2, ctx);
> + emit_alu3(SDIV, dst, src, tmp, ctx);
> + } else {
> + emit_write_y(G0, ctx);
> + emit_alu3(DIV, dst, src, tmp, ctx);
> + }
> emit_alu3(MULX, tmp, src, tmp, ctx);
> emit_alu3(SUB, dst, tmp, dst, ctx);
> goto do_alu32_trunc;
> }
> case BPF_ALU64 | BPF_MOD | BPF_X: {
> const u8 tmp = bpf2sparc[TMP_REG_1];
> + const unsigned int mod = (off == 1) ? SDIVX : UDIVX;
>
> ctx->tmp_1_used = true;
>
> - emit_alu3(UDIVX, dst, src, tmp, ctx);
> + emit_alu3(mod, dst, src, tmp, ctx);
> emit_alu3(MULX, tmp, src, tmp, ctx);
> emit_alu3(SUB, dst, tmp, dst, ctx);
> break;
> @@ -1096,33 +1135,62 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
> case BPF_ALU64 | BPF_MUL | BPF_K:
> emit_alu_K(MULX, dst, imm, ctx);
> break;
> - case BPF_ALU | BPF_DIV | BPF_K:
> + case BPF_ALU | BPF_DIV | BPF_K: {
> + const bool is_signed = (off == 1);
> +
> if (imm == 0)
> return -EINVAL;
>
> - emit_write_y(G0, ctx);
> - emit_alu_K(DIV, dst, imm, ctx);
> + if (is_signed) {
> + const u8 tmp = bpf2sparc[TMP_REG_2];
> +
> + ctx->tmp_2_used = true;
> +
> + /* Sign-extend dst into %y for 32-bit sdiv. */
> + emit_alu3_K(SRA, dst, 31, tmp, ctx);
> + emit_write_y(tmp, ctx);
> + emit_alu_K(SDIV, dst, imm, ctx);
> + } else {
> + emit_write_y(G0, ctx);
> + emit_alu_K(DIV, dst, imm, ctx);
> + }
> goto do_alu32_trunc;
> + }
> case BPF_ALU64 | BPF_DIV | BPF_K:
> if (imm == 0)
> return -EINVAL;
>
> - emit_alu_K(UDIVX, dst, imm, ctx);
> + emit_alu_K((off == 1) ? SDIVX : UDIVX, dst, imm, ctx);
> break;
> case BPF_ALU64 | BPF_MOD | BPF_K:
> case BPF_ALU | BPF_MOD | BPF_K: {
> const u8 tmp = bpf2sparc[TMP_REG_2];
> + const bool is_signed = (off == 1);
> unsigned int div;
>
> if (imm == 0)
> return -EINVAL;
>
> - div = (BPF_CLASS(code) == BPF_ALU64) ? UDIVX : DIV;
> + if (BPF_CLASS(code) == BPF_ALU64)
> + div = is_signed ? SDIVX : UDIVX;
> + else
> + div = is_signed ? SDIV : DIV;
>
> ctx->tmp_2_used = true;
>
> - if (BPF_CLASS(code) != BPF_ALU64)
> - emit_write_y(G0, ctx);
> + if (BPF_CLASS(code) != BPF_ALU64) {
> + if (is_signed) {
> + const u8 tmp3 = bpf2sparc[TMP_REG_3];
> +
> + ctx->tmp_3_used = true;
> +
> + /* Sign-extend dst into %y for 32-bit sdiv. */
> + emit_alu3_K(SRA, dst, 31, tmp3, ctx);
> + emit_write_y(tmp3, ctx);
> + } else {
> + emit_write_y(G0, ctx);
> + }
> + }
> if (is_simm13(imm)) {
> emit(div | IMMED | RS1(dst) | S13(imm) | RD(tmp), ctx);
> emit(MULX | IMMED | RS1(tmp) | S13(imm) | RD(tmp), ctx);
>
> base-commit: d1f4b56417a3dc1a0600f960b14f46bd25eda89d
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH bpf-next] bpf, sparc: Add support for BPF_SDIV and BPF_SMOD in the sparc64 JIT
2026-07-29 0:33 ` Kuan-Wei Chiu
@ 2026-07-29 1:36 ` Nicholas Dudar
0 siblings, 0 replies; 3+ messages in thread
From: Nicholas Dudar @ 2026-07-29 1:36 UTC (permalink / raw)
To: Kuan-Wei Chiu
Cc: ast, daniel, andrii, eddyz87, memxor, davem, andreas, bpf,
sparclinux, linux-kernel
On Wed, Jul 29, 2026 at 08:33:15AM +0800, Kuan-Wei Chiu wrote:
> I saw that your bpf jit patches span multiple architectures.
> Assuming you don't have access to all these different hardware platforms.
> Just out of curiosity how were these patches tested?
Correct, I used QEMU for my local testing. For sparc64 I cross-compiled
bpf-next 520d7d794202 with sparc64-linux-gnu-gcc 11.4.0, then compared
complete lib/test_bpf runs from stock and patched kernels under
qemu-system-sparc64 -M sun4u. Ten tests changed from FAIL to PASS.
I also compared stock and patched runs for x86_32, parisc32/64 and
MIPS32/64, including MIPS R6.
For LoongArch I reproduced the issue under QEMU and Tiezhu Yang later
ran test_bpf with the patch and supplied Tested-by.
Thanks,
Nicholas
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-29 1:36 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-16 15:11 [PATCH bpf-next] bpf, sparc: Add support for BPF_SDIV and BPF_SMOD in the sparc64 JIT Nicholas Dudar
2026-07-29 0:33 ` Kuan-Wei Chiu
2026-07-29 1:36 ` Nicholas Dudar
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.