* [PATCH bpf-next v2 0/2] bpf, mips: Add signed div/mod support
@ 2026-07-29 16:29 Nicholas Dudar
2026-07-29 16:29 ` [PATCH bpf-next v2 1/2] bpf, mips: Factor out div/mod emission helpers Nicholas Dudar
2026-07-29 16:29 ` [PATCH bpf-next v2 2/2] bpf, mips: Add support for BPF_SDIV and BPF_SMOD Nicholas Dudar
0 siblings, 2 replies; 6+ messages in thread
From: Nicholas Dudar @ 2026-07-29 16:29 UTC (permalink / raw)
To: ast, daniel, andrii, eddyz87, memxor, tsbogend, johan.almbladh,
paulburton
Cc: martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai,
philmd, visitorckw, bpf, linux-mips, linux-kernel
The MIPS JITs select division and modulo implementations from the BPF
class and opcode, but do not inspect insn->off. BPF_SDIV and BPF_SMOD
therefore use unsigned instructions, or unsigned C helpers for ALU64
on 32-bit MIPS.
Patch 1 factors division and modulo emission into helpers. Patch 2
uses insn->off to select the signed instructions and helpers.
Tested with test_bpf under QEMU on MIPS32r2, MIPS64r2, MIPS32r6, and
MIPS64r6, with CONFIG_BPF_JIT_ALWAYS_ON=y. Patch 1 produced the base
failure set on each target. With both patches, all 16 SDIV/SMOD cases
passed. Fourteen failed on the base; two already passed. No test
changed from PASS to FAIL.
v2:
- split out the div/mod helpers as Philippe suggested
- use an unsigned quotient intermediate in the signed remainder helper
- rebase onto bpf-next at fdec474c65fd
- test the base, patch 1, and the full series on four MIPS targets
v1: https://lore.kernel.org/bpf/20260716211055.2569433-1-main.kalliope@gmail.com/
Nicholas Dudar (2):
bpf, mips: Factor out div/mod emission helpers
bpf, mips: Add support for BPF_SDIV and BPF_SMOD
arch/mips/include/asm/uasm.h | 6 +++
arch/mips/mm/uasm-mips.c | 10 +++++
arch/mips/mm/uasm.c | 20 ++++++---
arch/mips/net/bpf_jit_comp.c | 53 +++++++++++++++++-------
arch/mips/net/bpf_jit_comp.h | 5 ++-
arch/mips/net/bpf_jit_comp32.c | 30 +++++++++-----
arch/mips/net/bpf_jit_comp64.c | 74 +++++++++++++++++++++++-----------
7 files changed, 143 insertions(+), 55 deletions(-)
base-commit: fdec474c65fd35d5a6e1497ed50a9f98c07192f0
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH bpf-next v2 1/2] bpf, mips: Factor out div/mod emission helpers
2026-07-29 16:29 [PATCH bpf-next v2 0/2] bpf, mips: Add signed div/mod support Nicholas Dudar
@ 2026-07-29 16:29 ` Nicholas Dudar
2026-07-29 17:10 ` bot+bpf-ci
2026-07-29 16:29 ` [PATCH bpf-next v2 2/2] bpf, mips: Add support for BPF_SDIV and BPF_SMOD Nicholas Dudar
1 sibling, 1 reply; 6+ messages in thread
From: Nicholas Dudar @ 2026-07-29 16:29 UTC (permalink / raw)
To: ast, daniel, andrii, eddyz87, memxor, tsbogend, johan.almbladh,
paulburton
Cc: martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai,
philmd, visitorckw, bpf, linux-mips, linux-kernel
Factor MIPS32 and MIPS64 division and modulo emission out of
emit_alu_r() and emit_alu_r64(). This prepares the JITs to select
signed or unsigned opcodes without duplicating the R6 and pre-R6
handling.
No functional change intended.
Suggested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Nicholas Dudar <main.kalliope@gmail.com>
---
arch/mips/net/bpf_jit_comp.c | 34 ++++++++++++++++++++++------------
arch/mips/net/bpf_jit_comp64.c | 34 ++++++++++++++++++++++------------
2 files changed, 44 insertions(+), 24 deletions(-)
diff --git a/arch/mips/net/bpf_jit_comp.c b/arch/mips/net/bpf_jit_comp.c
index 6ee4abe6a..f99bb5705 100644
--- a/arch/mips/net/bpf_jit_comp.c
+++ b/arch/mips/net/bpf_jit_comp.c
@@ -338,6 +338,26 @@ void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
clobber_reg(ctx, dst);
}
+static void emit_div(struct jit_context *ctx, u8 dst, u8 src)
+{
+ if (cpu_has_mips32r6) {
+ emit(ctx, divu_r6, dst, dst, src);
+ } else {
+ emit(ctx, divu, dst, src);
+ emit(ctx, mflo, dst);
+ }
+}
+
+static void emit_mod(struct jit_context *ctx, u8 dst, u8 src)
+{
+ if (cpu_has_mips32r6) {
+ emit(ctx, modu, dst, dst, src);
+ } else {
+ emit(ctx, divu, dst, src);
+ emit(ctx, mfhi, dst);
+ }
+}
+
/* ALU register operation (32-bit) */
void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op)
{
@@ -385,21 +405,11 @@ void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op)
break;
/* dst = dst / src */
case BPF_DIV:
- if (cpu_has_mips32r6) {
- emit(ctx, divu_r6, dst, dst, src);
- } else {
- emit(ctx, divu, dst, src);
- emit(ctx, mflo, dst);
- }
+ emit_div(ctx, dst, src);
break;
/* dst = dst % src */
case BPF_MOD:
- if (cpu_has_mips32r6) {
- emit(ctx, modu, dst, dst, src);
- } else {
- emit(ctx, divu, dst, src);
- emit(ctx, mfhi, dst);
- }
+ emit_mod(ctx, dst, src);
break;
}
clobber_reg(ctx, dst);
diff --git a/arch/mips/net/bpf_jit_comp64.c b/arch/mips/net/bpf_jit_comp64.c
index fa7e9aa37..befeb63ae 100644
--- a/arch/mips/net/bpf_jit_comp64.c
+++ b/arch/mips/net/bpf_jit_comp64.c
@@ -197,6 +197,26 @@ static void emit_alu_i64(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
clobber_reg(ctx, dst);
}
+static void emit_div64(struct jit_context *ctx, u8 dst, u8 src)
+{
+ if (cpu_has_mips64r6) {
+ emit(ctx, ddivu_r6, dst, dst, src);
+ } else {
+ emit(ctx, ddivu, dst, src);
+ emit(ctx, mflo, dst);
+ }
+}
+
+static void emit_mod64(struct jit_context *ctx, u8 dst, u8 src)
+{
+ if (cpu_has_mips64r6) {
+ emit(ctx, dmodu, dst, dst, src);
+ } else {
+ emit(ctx, ddivu, dst, src);
+ emit(ctx, mfhi, dst);
+ }
+}
+
/* ALU register operation (64-bit) */
static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op)
{
@@ -235,21 +255,11 @@ static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op)
break;
/* dst = dst / src */
case BPF_DIV:
- if (cpu_has_mips64r6) {
- emit(ctx, ddivu_r6, dst, dst, src);
- } else {
- emit(ctx, ddivu, dst, src);
- emit(ctx, mflo, dst);
- }
+ emit_div64(ctx, dst, src);
break;
/* dst = dst % src */
case BPF_MOD:
- if (cpu_has_mips64r6) {
- emit(ctx, dmodu, dst, dst, src);
- } else {
- emit(ctx, ddivu, dst, src);
- emit(ctx, mfhi, dst);
- }
+ emit_mod64(ctx, dst, src);
break;
default:
/* Width-generic operations */
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH bpf-next v2 2/2] bpf, mips: Add support for BPF_SDIV and BPF_SMOD
2026-07-29 16:29 [PATCH bpf-next v2 0/2] bpf, mips: Add signed div/mod support Nicholas Dudar
2026-07-29 16:29 ` [PATCH bpf-next v2 1/2] bpf, mips: Factor out div/mod emission helpers Nicholas Dudar
@ 2026-07-29 16:29 ` Nicholas Dudar
1 sibling, 0 replies; 6+ messages in thread
From: Nicholas Dudar @ 2026-07-29 16:29 UTC (permalink / raw)
To: ast, daniel, andrii, eddyz87, memxor, tsbogend, johan.almbladh,
paulburton
Cc: martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai,
philmd, visitorckw, bpf, linux-mips, linux-kernel
The MIPS JITs handle BPF_DIV and BPF_MOD without inspecting
insn->off, which distinguishes BPF_SDIV and BPF_SMOD. Signed
operations therefore use unsigned instructions, or unsigned helpers
for ALU64 on 32-bit MIPS, and produce unsigned results for negative
operands.
Use insn->off to select signed DIV/DDIV for pre-R6 and
DIV/MOD/DDIV/DMOD for R6. On 32-bit MIPS, use div64_s64() and a
signed remainder helper for ALU64. Add the corresponding uasm
emitters, and keep signed immediates out of the unsigned
power-of-two shift/mask rewrite.
The eBPF JIT is not available for microMIPS, so uasm-micromips does
not need matching emitters.
check_alu_op() rejects immediate zero divisors. bpf_do_misc_fixups()
rewrites signed immediate -1 and guards register divisors that are
zero or, for signed operations, -1 before JIT compilation. The ALU32
paths continue to zero-extend their results.
Found while auditing BPF JIT handling of signed div/mod. Tested with
test_bpf on the v7.2-rc4 bpf-next tree under QEMU on MIPS32r2,
MIPS64r2, MIPS32r6, and MIPS64r6 with
CONFIG_BPF_JIT_ALWAYS_ON=y.
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Nicholas Dudar <main.kalliope@gmail.com>
---
arch/mips/include/asm/uasm.h | 6 ++++
arch/mips/mm/uasm-mips.c | 10 ++++++
arch/mips/mm/uasm.c | 20 ++++++++----
arch/mips/net/bpf_jit_comp.c | 35 +++++++++++++++------
arch/mips/net/bpf_jit_comp.h | 5 +--
arch/mips/net/bpf_jit_comp32.c | 30 ++++++++++++------
arch/mips/net/bpf_jit_comp64.c | 56 ++++++++++++++++++++++------------
7 files changed, 115 insertions(+), 47 deletions(-)
diff --git a/arch/mips/include/asm/uasm.h b/arch/mips/include/asm/uasm.h
index b43bfd445..9d3edb59e 100644
--- a/arch/mips/include/asm/uasm.h
+++ b/arch/mips/include/asm/uasm.h
@@ -85,15 +85,20 @@ Ip_u1u2(_ctc1);
Ip_u2u1(_ctcmsa);
Ip_u2u1s3(_daddiu);
Ip_u3u1u2(_daddu);
+Ip_u1u2(_ddiv);
+Ip_u3u1u2(_ddiv_r6);
Ip_u1u2(_ddivu);
Ip_u3u1u2(_ddivu_r6);
Ip_u1(_di);
Ip_u2u1msbu3(_dins);
Ip_u2u1msbu3(_dinsm);
Ip_u2u1msbu3(_dinsu);
+Ip_u1u2(_div);
+Ip_u3u1u2(_div_r6);
Ip_u1u2(_divu);
Ip_u3u1u2(_divu_r6);
Ip_u1u2u3(_dmfc0);
+Ip_u3u1u2(_dmod);
Ip_u3u1u2(_dmodu);
Ip_u1u2u3(_dmtc0);
Ip_u1u2(_dmultu);
@@ -135,6 +140,7 @@ Ip_u1u2u3(_mfc0);
Ip_u1u2u3(_mfhc0);
Ip_u1(_mfhi);
Ip_u1(_mflo);
+Ip_u3u1u2(_mod);
Ip_u3u1u2(_modu);
Ip_u3u1u2(_movn);
Ip_u3u1u2(_movz);
diff --git a/arch/mips/mm/uasm-mips.c b/arch/mips/mm/uasm-mips.c
index e15c6700c..ef1a2b040 100644
--- a/arch/mips/mm/uasm-mips.c
+++ b/arch/mips/mm/uasm-mips.c
@@ -75,6 +75,9 @@ static const struct insn insn_table[insn_invalid] = {
[insn_ctcmsa] = {M(msa_op, 0, msa_ctc_op, 0, 0, msa_elm_op), RD | RE},
[insn_daddiu] = {M(daddiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM},
[insn_daddu] = {M(spec_op, 0, 0, 0, 0, daddu_op), RS | RT | RD},
+ [insn_ddiv] = {M(spec_op, 0, 0, 0, 0, ddiv_op), RS | RT},
+ [insn_ddiv_r6] = {M(spec_op, 0, 0, 0, ddiv_ddiv6_op, ddiv_op),
+ RS | RT | RD},
[insn_ddivu] = {M(spec_op, 0, 0, 0, 0, ddivu_op), RS | RT},
[insn_ddivu_r6] = {M(spec_op, 0, 0, 0, ddivu_ddivu6_op, ddivu_op),
RS | RT | RD},
@@ -82,10 +85,15 @@ static const struct insn insn_table[insn_invalid] = {
[insn_dins] = {M(spec3_op, 0, 0, 0, 0, dins_op), RS | RT | RD | RE},
[insn_dinsm] = {M(spec3_op, 0, 0, 0, 0, dinsm_op), RS | RT | RD | RE},
[insn_dinsu] = {M(spec3_op, 0, 0, 0, 0, dinsu_op), RS | RT | RD | RE},
+ [insn_div] = {M(spec_op, 0, 0, 0, 0, div_op), RS | RT},
+ [insn_div_r6] = {M(spec_op, 0, 0, 0, div_div6_op, div_op),
+ RS | RT | RD},
[insn_divu] = {M(spec_op, 0, 0, 0, 0, divu_op), RS | RT},
[insn_divu_r6] = {M(spec_op, 0, 0, 0, divu_divu6_op, divu_op),
RS | RT | RD},
[insn_dmfc0] = {M(cop0_op, dmfc_op, 0, 0, 0, 0), RT | RD | SET},
+ [insn_dmod] = {M(spec_op, 0, 0, 0, ddiv_dmod_op, ddiv_op),
+ RS | RT | RD},
[insn_dmodu] = {M(spec_op, 0, 0, 0, ddivu_dmodu_op, ddivu_op),
RS | RT | RD},
[insn_dmtc0] = {M(cop0_op, dmtc_op, 0, 0, 0, 0), RT | RD | SET},
@@ -140,6 +148,8 @@ static const struct insn insn_table[insn_invalid] = {
[insn_mfhc0] = {M(cop0_op, mfhc0_op, 0, 0, 0, 0), RT | RD | SET},
[insn_mfhi] = {M(spec_op, 0, 0, 0, 0, mfhi_op), RD},
[insn_mflo] = {M(spec_op, 0, 0, 0, 0, mflo_op), RD},
+ [insn_mod] = {M(spec_op, 0, 0, 0, div_mod_op, div_op),
+ RS | RT | RD},
[insn_modu] = {M(spec_op, 0, 0, 0, divu_modu_op, divu_op),
RS | RT | RD},
[insn_movn] = {M(spec_op, 0, 0, 0, 0, movn_op), RS | RT | RD},
diff --git a/arch/mips/mm/uasm.c b/arch/mips/mm/uasm.c
index 125140979..06d722665 100644
--- a/arch/mips/mm/uasm.c
+++ b/arch/mips/mm/uasm.c
@@ -49,18 +49,20 @@ enum opcode {
insn_addiu, insn_addu, insn_and, insn_andi, insn_bbit0, insn_bbit1,
insn_beq, insn_beql, insn_bgez, insn_bgezl, insn_bgtz, insn_blez,
insn_bltz, insn_bltzl, insn_bne, insn_break, insn_cache, insn_cfc1,
- insn_cfcmsa, insn_ctc1, insn_ctcmsa, insn_daddiu, insn_daddu, insn_ddivu,
- insn_ddivu_r6, insn_di, insn_dins, insn_dinsm, insn_dinsu, insn_divu,
- insn_divu_r6, insn_dmfc0, insn_dmodu, insn_dmtc0, insn_dmultu,
+ insn_cfcmsa, insn_ctc1, insn_ctcmsa, insn_daddiu, insn_daddu, insn_ddiv,
+ insn_ddiv_r6, insn_ddivu, insn_ddivu_r6, insn_di, insn_dins, insn_dinsm,
+ insn_dinsu, insn_div, insn_div_r6, insn_divu, insn_divu_r6, insn_dmfc0,
+ insn_dmod, insn_dmodu, insn_dmtc0, insn_dmultu,
insn_dmulu, insn_drotr, insn_drotr32, insn_dsbh, insn_dshd, insn_dsll,
insn_dsll32, insn_dsllv, insn_dsra, insn_dsra32, insn_dsrav, insn_dsrl,
insn_dsrl32, insn_dsrlv, insn_dsubu, insn_eret, insn_ext, insn_ins,
insn_j, insn_jal, insn_jalr, insn_jr, insn_lb, insn_lbu, insn_ld,
insn_lddir, insn_ldpte, insn_ldx, insn_lh, insn_lhu, insn_ll, insn_lld,
insn_lui, insn_lw, insn_lwu, insn_lwx, insn_mfc0, insn_mfhc0, insn_mfhi,
- insn_mflo, insn_modu, insn_movn, insn_movz, insn_mtc0, insn_mthc0,
- insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_mulu, insn_muhu, insn_nor,
- insn_or, insn_ori, insn_pref, insn_rfe, insn_rotr, insn_sb, insn_sc,
+ insn_mflo, insn_mod, insn_modu, insn_movn, insn_movz, insn_mtc0,
+ insn_mthc0, insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_mulu,
+ insn_muhu, insn_nor, insn_or, insn_ori, insn_pref, insn_rfe, insn_rotr,
+ insn_sb, insn_sc,
insn_scd, insn_seleqz, insn_selnez, insn_sd, insn_sh, insn_sll,
insn_sllv, insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra,
insn_srav, insn_srl, insn_srlv, insn_subu, insn_sw, insn_sync,
@@ -287,9 +289,12 @@ I_u1u2(_cfc1)
I_u2u1(_cfcmsa)
I_u1u2(_ctc1)
I_u2u1(_ctcmsa)
+I_u1u2(_ddiv)
+I_u3u1u2(_ddiv_r6)
I_u1u2(_ddivu)
I_u3u1u2(_ddivu_r6)
I_u1u2u3(_dmfc0)
+I_u3u1u2(_dmod)
I_u3u1u2(_dmodu)
I_u1u2u3(_dmtc0)
I_u1u2(_dmultu)
@@ -297,6 +302,8 @@ I_u3u1u2(_dmulu)
I_u2u1s3(_daddiu)
I_u3u1u2(_daddu)
I_u1(_di);
+I_u1u2(_div)
+I_u3u1u2(_div_r6)
I_u1u2(_divu)
I_u3u1u2(_divu_r6)
I_u2u1(_dsbh);
@@ -332,6 +339,7 @@ I_u2s3u1(_lw)
I_u2s3u1(_lwu)
I_u1u2u3(_mfc0)
I_u1u2u3(_mfhc0)
+I_u3u1u2(_mod)
I_u3u1u2(_modu)
I_u3u1u2(_movn)
I_u3u1u2(_movz)
diff --git a/arch/mips/net/bpf_jit_comp.c b/arch/mips/net/bpf_jit_comp.c
index f99bb5705..a67885d28 100644
--- a/arch/mips/net/bpf_jit_comp.c
+++ b/arch/mips/net/bpf_jit_comp.c
@@ -208,7 +208,7 @@ void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src)
}
/* Validate ALU immediate range */
-bool valid_alu_i(u8 op, s32 imm)
+bool valid_alu_i(u8 op, s32 imm, bool is_signed)
{
switch (BPF_OP(op)) {
case BPF_NEG:
@@ -237,6 +237,9 @@ bool valid_alu_i(u8 op, s32 imm)
return imm == 0 || (imm > 0 && is_power_of_2(imm));
case BPF_DIV:
case BPF_MOD:
+ /* Do not use unsigned shift/mask rewrites for signed div/mod. */
+ if (is_signed)
+ return false;
/* imm must be an 17-bit power of two */
return (u32)imm <= 0x10000 && is_power_of_2((u32)imm);
}
@@ -338,28 +341,40 @@ void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
clobber_reg(ctx, dst);
}
-static void emit_div(struct jit_context *ctx, u8 dst, u8 src)
+static void emit_div(struct jit_context *ctx, u8 dst, u8 src, bool is_signed)
{
if (cpu_has_mips32r6) {
- emit(ctx, divu_r6, dst, dst, src);
+ if (is_signed)
+ emit(ctx, div_r6, dst, dst, src);
+ else
+ emit(ctx, divu_r6, dst, dst, src);
} else {
- emit(ctx, divu, dst, src);
+ if (is_signed)
+ emit(ctx, div, dst, src);
+ else
+ emit(ctx, divu, dst, src);
emit(ctx, mflo, dst);
}
}
-static void emit_mod(struct jit_context *ctx, u8 dst, u8 src)
+static void emit_mod(struct jit_context *ctx, u8 dst, u8 src, bool is_signed)
{
if (cpu_has_mips32r6) {
- emit(ctx, modu, dst, dst, src);
+ if (is_signed)
+ emit(ctx, mod, dst, dst, src);
+ else
+ emit(ctx, modu, dst, dst, src);
} else {
- emit(ctx, divu, dst, src);
+ if (is_signed)
+ emit(ctx, div, dst, src);
+ else
+ emit(ctx, divu, dst, src);
emit(ctx, mfhi, dst);
}
}
/* ALU register operation (32-bit) */
-void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op)
+void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op, bool is_signed)
{
switch (BPF_OP(op)) {
/* dst = dst & src */
@@ -405,11 +420,11 @@ void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op)
break;
/* dst = dst / src */
case BPF_DIV:
- emit_div(ctx, dst, src);
+ emit_div(ctx, dst, src, is_signed);
break;
/* dst = dst % src */
case BPF_MOD:
- emit_mod(ctx, dst, src);
+ emit_mod(ctx, dst, src, is_signed);
break;
}
clobber_reg(ctx, dst);
diff --git a/arch/mips/net/bpf_jit_comp.h b/arch/mips/net/bpf_jit_comp.h
index a37fe2081..39266daa6 100644
--- a/arch/mips/net/bpf_jit_comp.h
+++ b/arch/mips/net/bpf_jit_comp.h
@@ -163,7 +163,7 @@ void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm);
void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src);
/* Validate ALU/ALU64 immediate range */
-bool valid_alu_i(u8 op, s32 imm);
+bool valid_alu_i(u8 op, s32 imm, bool is_signed);
/* Rewrite ALU/ALU64 immediate operation */
bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val);
@@ -172,7 +172,8 @@ bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val);
void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op);
/* ALU register operation (32-bit) */
-void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op);
+void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op,
+ bool is_signed);
/* Atomic read-modify-write (32-bit) */
void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code);
diff --git a/arch/mips/net/bpf_jit_comp32.c b/arch/mips/net/bpf_jit_comp32.c
index 40a878b67..c8d45a5d5 100644
--- a/arch/mips/net/bpf_jit_comp32.c
+++ b/arch/mips/net/bpf_jit_comp32.c
@@ -512,7 +512,7 @@ static void emit_mul_r64(struct jit_context *ctx,
clobber_reg64(ctx, dst);
}
-/* Helper function for 64-bit modulo */
+/* Helper function for unsigned 64-bit modulo */
static u64 jit_mod64(u64 a, u64 b)
{
u64 rem;
@@ -521,9 +521,17 @@ static u64 jit_mod64(u64 a, u64 b)
return rem;
}
+/* Helper function for signed 64-bit modulo */
+static s64 jit_smod64(s64 a, s64 b)
+{
+ u64 quot = div64_s64(a, b);
+
+ return a - quot * b;
+}
+
/* ALU div/mod register (64-bit) */
-static void emit_divmod_r64(struct jit_context *ctx,
- const u8 dst[], const u8 src[], u8 op)
+static void emit_divmod_r64(struct jit_context *ctx, const u8 dst[],
+ const u8 src[], u8 op, bool is_signed)
{
const u8 *r0 = bpf2mips32[BPF_REG_0]; /* Mapped to v0-v1 */
const u8 *r1 = bpf2mips32[BPF_REG_1]; /* Mapped to a0-a1 */
@@ -546,11 +554,11 @@ static void emit_divmod_r64(struct jit_context *ctx,
switch (BPF_OP(op)) {
/* dst = dst / src */
case BPF_DIV:
- addr = (u32)&div64_u64;
+ addr = is_signed ? (u32)&div64_s64 : (u32)&div64_u64;
break;
/* dst = dst % src */
case BPF_MOD:
- addr = (u32)&jit_mod64;
+ addr = is_signed ? (u32)&jit_smod64 : (u32)&jit_mod64;
break;
}
emit_mov_i(ctx, MIPS_R_T9, addr);
@@ -1469,6 +1477,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
u8 code = insn->code;
s16 off = insn->off;
s32 imm = insn->imm;
+ bool is_signed = off == 1;
s32 val, rel;
u8 alu, jmp;
@@ -1516,9 +1525,10 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_MUL | BPF_K:
case BPF_ALU | BPF_DIV | BPF_K:
case BPF_ALU | BPF_MOD | BPF_K:
- if (!valid_alu_i(BPF_OP(code), imm)) {
+ if (!valid_alu_i(BPF_OP(code), imm, is_signed)) {
emit_mov_i(ctx, MIPS_R_T6, imm);
- emit_alu_r(ctx, lo(dst), MIPS_R_T6, BPF_OP(code));
+ emit_alu_r(ctx, lo(dst), MIPS_R_T6, BPF_OP(code),
+ is_signed);
} else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
emit_alu_i(ctx, lo(dst), val, alu);
}
@@ -1546,7 +1556,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_MUL | BPF_X:
case BPF_ALU | BPF_DIV | BPF_X:
case BPF_ALU | BPF_MOD | BPF_X:
- emit_alu_r(ctx, lo(dst), lo(src), BPF_OP(code));
+ emit_alu_r(ctx, lo(dst), lo(src), BPF_OP(code), is_signed);
emit_zext_ver(ctx, dst);
break;
/* dst = imm (64-bit) */
@@ -1599,7 +1609,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
* and then do the operation on this register.
*/
emit_mov_se_i64(ctx, tmp, imm);
- emit_divmod_r64(ctx, dst, tmp, BPF_OP(code));
+ emit_divmod_r64(ctx, dst, tmp, BPF_OP(code), is_signed);
break;
/* dst = dst & src (64-bit) */
/* dst = dst | src (64-bit) */
@@ -1629,7 +1639,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
/* dst = dst % src (64-bit) */
case BPF_ALU64 | BPF_DIV | BPF_X:
case BPF_ALU64 | BPF_MOD | BPF_X:
- emit_divmod_r64(ctx, dst, src, BPF_OP(code));
+ emit_divmod_r64(ctx, dst, src, BPF_OP(code), is_signed);
break;
/* dst = htole(dst) */
/* dst = htobe(dst) */
diff --git a/arch/mips/net/bpf_jit_comp64.c b/arch/mips/net/bpf_jit_comp64.c
index befeb63ae..ca2741f14 100644
--- a/arch/mips/net/bpf_jit_comp64.c
+++ b/arch/mips/net/bpf_jit_comp64.c
@@ -197,28 +197,43 @@ static void emit_alu_i64(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
clobber_reg(ctx, dst);
}
-static void emit_div64(struct jit_context *ctx, u8 dst, u8 src)
+static void emit_div64(struct jit_context *ctx, u8 dst, u8 src,
+ bool is_signed)
{
if (cpu_has_mips64r6) {
- emit(ctx, ddivu_r6, dst, dst, src);
+ if (is_signed)
+ emit(ctx, ddiv_r6, dst, dst, src);
+ else
+ emit(ctx, ddivu_r6, dst, dst, src);
} else {
- emit(ctx, ddivu, dst, src);
+ if (is_signed)
+ emit(ctx, ddiv, dst, src);
+ else
+ emit(ctx, ddivu, dst, src);
emit(ctx, mflo, dst);
}
}
-static void emit_mod64(struct jit_context *ctx, u8 dst, u8 src)
+static void emit_mod64(struct jit_context *ctx, u8 dst, u8 src,
+ bool is_signed)
{
if (cpu_has_mips64r6) {
- emit(ctx, dmodu, dst, dst, src);
+ if (is_signed)
+ emit(ctx, dmod, dst, dst, src);
+ else
+ emit(ctx, dmodu, dst, dst, src);
} else {
- emit(ctx, ddivu, dst, src);
+ if (is_signed)
+ emit(ctx, ddiv, dst, src);
+ else
+ emit(ctx, ddivu, dst, src);
emit(ctx, mfhi, dst);
}
}
/* ALU register operation (64-bit) */
-static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op)
+static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op,
+ bool is_signed)
{
switch (BPF_OP(op)) {
/* dst = dst << src */
@@ -255,15 +270,15 @@ static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op)
break;
/* dst = dst / src */
case BPF_DIV:
- emit_div64(ctx, dst, src);
+ emit_div64(ctx, dst, src, is_signed);
break;
/* dst = dst % src */
case BPF_MOD:
- emit_mod64(ctx, dst, src);
+ emit_mod64(ctx, dst, src, is_signed);
break;
default:
/* Width-generic operations */
- emit_alu_r(ctx, dst, src, op);
+ emit_alu_r(ctx, dst, src, op, false);
}
clobber_reg(ctx, dst);
}
@@ -650,6 +665,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
u8 code = insn->code;
s16 off = insn->off;
s32 imm = insn->imm;
+ bool is_signed = off == 1;
s32 val, rel;
u8 alu, jmp;
@@ -684,9 +700,9 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_AND | BPF_K:
case BPF_ALU | BPF_XOR | BPF_K:
case BPF_ALU | BPF_LSH | BPF_K:
- if (!valid_alu_i(BPF_OP(code), imm)) {
+ if (!valid_alu_i(BPF_OP(code), imm, false)) {
emit_mov_i(ctx, MIPS_R_T4, imm);
- emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
+ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code), false);
} else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
emit_alu_i(ctx, dst, val, alu);
}
@@ -706,10 +722,11 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_MUL | BPF_K:
case BPF_ALU | BPF_DIV | BPF_K:
case BPF_ALU | BPF_MOD | BPF_K:
- if (!valid_alu_i(BPF_OP(code), imm)) {
+ if (!valid_alu_i(BPF_OP(code), imm, is_signed)) {
emit_sext(ctx, dst, dst);
emit_mov_i(ctx, MIPS_R_T4, imm);
- emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
+ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code),
+ is_signed);
} else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
emit_sext(ctx, dst, dst);
emit_alu_i(ctx, dst, val, alu);
@@ -724,7 +741,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_OR | BPF_X:
case BPF_ALU | BPF_XOR | BPF_X:
case BPF_ALU | BPF_LSH | BPF_X:
- emit_alu_r(ctx, dst, src, BPF_OP(code));
+ emit_alu_r(ctx, dst, src, BPF_OP(code), false);
emit_zext_ver(ctx, dst);
break;
/* dst = dst >> src */
@@ -743,7 +760,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU | BPF_MOD | BPF_X:
emit_sext(ctx, dst, dst);
emit_sext(ctx, MIPS_R_T4, src);
- emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code));
+ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code), is_signed);
emit_zext_ver(ctx, dst);
break;
/* dst = imm (64-bit) */
@@ -780,9 +797,10 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU64 | BPF_MUL | BPF_K:
case BPF_ALU64 | BPF_DIV | BPF_K:
case BPF_ALU64 | BPF_MOD | BPF_K:
- if (!valid_alu_i(BPF_OP(code), imm)) {
+ if (!valid_alu_i(BPF_OP(code), imm, is_signed)) {
emit_mov_i(ctx, MIPS_R_T4, imm);
- emit_alu_r64(ctx, dst, MIPS_R_T4, BPF_OP(code));
+ emit_alu_r64(ctx, dst, MIPS_R_T4, BPF_OP(code),
+ is_signed);
} else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) {
emit_alu_i64(ctx, dst, val, alu);
}
@@ -809,7 +827,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_ALU64 | BPF_MUL | BPF_X:
case BPF_ALU64 | BPF_DIV | BPF_X:
case BPF_ALU64 | BPF_MOD | BPF_X:
- emit_alu_r64(ctx, dst, src, BPF_OP(code));
+ emit_alu_r64(ctx, dst, src, BPF_OP(code), is_signed);
break;
/* dst = htole(dst) */
/* dst = htobe(dst) */
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpf, mips: Factor out div/mod emission helpers
2026-07-29 16:29 ` [PATCH bpf-next v2 1/2] bpf, mips: Factor out div/mod emission helpers Nicholas Dudar
@ 2026-07-29 17:10 ` bot+bpf-ci
2026-07-29 18:54 ` Nicholas Dudar
0 siblings, 1 reply; 6+ messages in thread
From: bot+bpf-ci @ 2026-07-29 17:10 UTC (permalink / raw)
To: main.kalliope, ast, daniel, andrii, eddyz87, memxor, tsbogend,
johan.almbladh, paulburton
Cc: martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai,
philmd, visitorckw, bpf, linux-mips, linux-kernel, ast, andrii,
daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2077 bytes --]
> diff --git a/arch/mips/net/bpf_jit_comp.c b/arch/mips/net/bpf_jit_comp.c
> index 6ee4abe6a1f7..f99bb5705d00 100644
> --- a/arch/mips/net/bpf_jit_comp.c
> +++ b/arch/mips/net/bpf_jit_comp.c
> @@ -338,6 +338,26 @@ void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
> clobber_reg(ctx, dst);
> }
>
> +static void emit_div(struct jit_context *ctx, u8 dst, u8 src)
> +{
> + if (cpu_has_mips32r6) {
> + emit(ctx, divu_r6, dst, dst, src);
> + } else {
> + emit(ctx, divu, dst, src);
> + emit(ctx, mflo, dst);
> + }
> +}
> +
> +static void emit_mod(struct jit_context *ctx, u8 dst, u8 src)
> +{
> + if (cpu_has_mips32r6) {
> + emit(ctx, modu, dst, dst, src);
> + } else {
> + emit(ctx, divu, dst, src);
> + emit(ctx, mfhi, dst);
> + }
> +}
This isn't a bug, but would a short header comment on emit_div() and
emit_mod(), in the style of the neighbouring '/* ALU register operation
(32-bit) */', keep these consistent with the rest of the file? Every
other emitter here carries one.
[ ... ]
> diff --git a/arch/mips/net/bpf_jit_comp64.c b/arch/mips/net/bpf_jit_comp64.c
> index fa7e9aa37f49..befeb63ae447 100644
> --- a/arch/mips/net/bpf_jit_comp64.c
> +++ b/arch/mips/net/bpf_jit_comp64.c
> @@ -197,6 +197,26 @@ static void emit_alu_i64(struct jit_context *ctx, u8 dst, s32 imm, u8 op)
> clobber_reg(ctx, dst);
> }
>
> +static void emit_div64(struct jit_context *ctx, u8 dst, u8 src)
> +{
> + if (cpu_has_mips64r6) {
> + emit(ctx, ddivu_r6, dst, dst, src);
> + } else {
> + emit(ctx, ddivu, dst, src);
> + emit(ctx, mflo, dst);
> + }
> +}
> +
> +static void emit_mod64(struct jit_context *ctx, u8 dst, u8 src)
> +{
> + if (cpu_has_mips64r6) {
> + emit(ctx, dmodu, dst, dst, src);
> + } else {
> + emit(ctx, ddivu, dst, src);
> + emit(ctx, mfhi, dst);
> + }
> +}
Same for emit_div64() and emit_mod64() here.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/30472129214
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpf, mips: Factor out div/mod emission helpers
2026-07-29 17:10 ` bot+bpf-ci
@ 2026-07-29 18:54 ` Nicholas Dudar
2026-07-30 8:11 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 6+ messages in thread
From: Nicholas Dudar @ 2026-07-29 18:54 UTC (permalink / raw)
To: bot+bpf-ci
Cc: ast, daniel, andrii, eddyz87, memxor, tsbogend, johan.almbladh,
paulburton, martin.lau, song, yonghong.song, jolsa, emil,
ihor.solodrai, philmd, visitorckw, bpf, linux-mips, linux-kernel,
martin.lau, clm
> This isn't a bug, but would a short header comment on emit_div() and
> emit_mod(), in the style of the neighbouring '/* ALU register operation
> (32-bit) */', keep these consistent with the rest of the file? Every
> other emitter here carries one.
>
> Same for emit_div64() and emit_mod64() here.
Agreed that short comments would match the surrounding emitter style.
I'll add them if the series needs another revision.
Nicholas
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpf, mips: Factor out div/mod emission helpers
2026-07-29 18:54 ` Nicholas Dudar
@ 2026-07-30 8:11 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 6+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-30 8:11 UTC (permalink / raw)
To: Nicholas Dudar
Cc: ast, daniel, andrii, eddyz87, memxor, tsbogend, johan.almbladh,
paulburton, martin.lau, song, yonghong.song, jolsa, emil,
ihor.solodrai, visitorckw, bpf, linux-mips, linux-kernel,
martin.lau, clm
On 29/7/26 20:54, Nicholas Dudar wrote:
>> This isn't a bug, but would a short header comment on emit_div() and
>> emit_mod(), in the style of the neighbouring '/* ALU register operation
>> (32-bit) */', keep these consistent with the rest of the file? Every
>> other emitter here carries one.
>>
>> Same for emit_div64() and emit_mod64() here.
>
> Agreed that short comments would match the surrounding emitter style.
> I'll add them if the series needs another revision.
Thanks for the v2.
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-07-30 8:11 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 16:29 [PATCH bpf-next v2 0/2] bpf, mips: Add signed div/mod support Nicholas Dudar
2026-07-29 16:29 ` [PATCH bpf-next v2 1/2] bpf, mips: Factor out div/mod emission helpers Nicholas Dudar
2026-07-29 17:10 ` bot+bpf-ci
2026-07-29 18:54 ` Nicholas Dudar
2026-07-30 8:11 ` Philippe Mathieu-Daudé
2026-07-29 16:29 ` [PATCH bpf-next v2 2/2] bpf, mips: Add support for BPF_SDIV and BPF_SMOD Nicholas Dudar
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox