Linux MIPS Architecture development
 help / color / mirror / Atom feed
* [PATCH bpf-next v2 0/2] bpf, mips: Add BPF_MOVSX support to the JITs
@ 2026-09-04 11:17 Nicholas Dudar
  2026-09-04 11:17 ` [PATCH bpf-next v2 1/2] bpf, mips: Factor register moves into helpers Nicholas Dudar
  2026-09-04 11:17 ` [PATCH bpf-next v2 2/2] bpf, mips: Add support for BPF_MOVSX in the JITs Nicholas Dudar
  0 siblings, 2 replies; 5+ messages in thread
From: Nicholas Dudar @ 2026-09-04 11:17 UTC (permalink / raw)
  To: ast, daniel, andrii, eddyz87, memxor, johan.almbladh, paulburton,
	tsbogend
  Cc: martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai, bpf,
	linux-mips, linux-kernel

The MIPS32 and MIPS64 JITs lower register BPF_MOVSX as an ordinary
MOV because their move paths ignore insn->off. The instruction is
therefore silently miscompiled instead of falling back to the
interpreter.

Patch 1 factors ALU32 and ALU64 register moves into backend-local
helpers. Patch 2 decodes the MOVSX width from insn->off in those
helpers, keeping MIPS32 register-pair handling and the existing ALU32
upper-half paths inside the move operation.

This is missing architecture support rather than a regression. Core
MOVSX support arrived after both backends, and the analogous RV32 change
(c6a08afdfe3a, "riscv, bpf: Add support for BPF_MOVSX in RV32 JIT")
went through bpf-next without a Fixes tag.

This series can be applied before or after the in-flight SDIV/SMOD
series. Both are semantic prerequisites for MEMSX. The ordering
discussion is here:

Link: https://lore.kernel.org/bpf/CAJZwKkis=3NGw9At0WfiZaRYEoMPbQfqJKN0e+vtRDvp4VY5Ng@mail.gmail.com/

Targeted MOVSX and MOV control testing under QEMU Malta covered MIPS32
R1, R2, and R6 and MIPS64 R2 in both endian configurations, plus MIPS64
R6 little endian. All 180 runs required the JIT and covered the baseline,
helper-only, and final series boundaries.

I also ran the complete unfiltered lib/test_bpf.c suite directly on the
final v2 candidate with CONFIG_BPF_JIT_ALWAYS_ON=y on little-endian
MIPS32 R2 and R6 and MIPS64 R2 and R6. The MIPS32 profiles returned
1036 passes and 26 failures; the MIPS64 profiles returned 1035 passes
and 26 failures. All five MOVSX cases passed on every profile. The 26
remaining failures were 14 SDIV/SMOD, 8 BSWAP, 3 MEMSX, and 1
JMP32_JA case.

Existing lib/test_bpf.c cases exercise all five valid MOVSX forms on
MIPS, while verifier_movsx.c covers the generic verifier contract.
No physical MIPS hardware was tested.

Changes requested by Johan Almbladh in review of RFC v1:

- Group the 8- and 16-bit cases and compute the shift from insn->off.
- Move clobber bookkeeping to common emitter tails.
- Avoid early returns and use emit_sext() for MIPS64 MOVSX32.

Other changes in v2:

- Document why width decoding belongs in the move helpers, the supported
  ISA floor, and why other MOV offsets cannot reach these paths.
- Run the complete test_bpf suite directly on v2 on four little-endian
  ISA profiles.

v1: https://lore.kernel.org/bpf/20260819010523.1057789-1-main.kalliope@gmail.com/

Nicholas Dudar (2):
  bpf, mips: Factor register moves into helpers
  bpf, mips: Add support for BPF_MOVSX in the JITs

 arch/mips/net/bpf_jit_comp32.c | 60 +++++++++++++++++++++++++++++++---
 arch/mips/net/bpf_jit_comp64.c | 56 +++++++++++++++++++++++++++++--
 2 files changed, 109 insertions(+), 7 deletions(-)


base-commit: 6655c409707ec8ce9ce0850ffe4fe02331fd4d9c

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH bpf-next v2 1/2] bpf, mips: Factor register moves into helpers
  2026-09-04 11:17 [PATCH bpf-next v2 0/2] bpf, mips: Add BPF_MOVSX support to the JITs Nicholas Dudar
@ 2026-09-04 11:17 ` Nicholas Dudar
  2026-09-04 12:29   ` bot+bpf-ci
  2026-09-04 11:17 ` [PATCH bpf-next v2 2/2] bpf, mips: Add support for BPF_MOVSX in the JITs Nicholas Dudar
  1 sibling, 1 reply; 5+ messages in thread
From: Nicholas Dudar @ 2026-09-04 11:17 UTC (permalink / raw)
  To: ast, daniel, andrii, eddyz87, memxor, johan.almbladh, paulburton,
	tsbogend
  Cc: martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai, bpf,
	linux-mips, linux-kernel

BPF_MOVSX carries its source width in insn->off. Both MIPS JITs
currently lower register MOV instructions directly in build_insn().
Keeping the forthcoming MOVSX width switch there would split the move
operation between dispatcher cases and backend emitters.

Factor ALU32 and ALU64 register moves into helpers for each backend and
route the existing MOV paths through them. The helpers own the complete
move operation, including MIPS32 register-pair handling and the ALU32
upper-half state. This keeps the following MOVSX width decoding inside
one per-backend, per-width-class move emitter rather than in dispatcher
case arms or generic primitives.

Keep the verifier-inserted zero-extension marker on its dedicated path.
No functional change is intended.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Nicholas Dudar <main.kalliope@gmail.com>
---
 arch/mips/net/bpf_jit_comp32.c | 22 ++++++++++++++++++----
 arch/mips/net/bpf_jit_comp64.c | 18 +++++++++++++++---
 2 files changed, 33 insertions(+), 7 deletions(-)

diff --git a/arch/mips/net/bpf_jit_comp32.c b/arch/mips/net/bpf_jit_comp32.c
index 40a878b672f5..bfe73b023983 100644
--- a/arch/mips/net/bpf_jit_comp32.c
+++ b/arch/mips/net/bpf_jit_comp32.c
@@ -190,6 +190,22 @@ static void emit_zext_ver(struct jit_context *ctx, const u8 dst[])
 	}
 }
 
+/* Register move operation (32-bit) */
+static void emit_mov_r32(struct jit_context *ctx, const u8 dst[],
+			 const u8 src[])
+{
+	emit_mov_r(ctx, lo(dst), lo(src));
+	emit_zext_ver(ctx, dst);
+}
+
+/* Register move operation (64-bit) */
+static void emit_mov_r64(struct jit_context *ctx, const u8 dst[],
+			 const u8 src[])
+{
+	emit_mov_r(ctx, lo(dst), lo(src));
+	emit_mov_r(ctx, hi(dst), hi(src));
+}
+
 /* Load delay slot, if ISA mandates it */
 static void emit_load_delay(struct jit_context *ctx)
 {
@@ -1485,8 +1501,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
 			/* Special mov32 for zext */
 			emit_mov_i(ctx, hi(dst), 0);
 		} else {
-			emit_mov_r(ctx, lo(dst), lo(src));
-			emit_zext_ver(ctx, dst);
+			emit_mov_r32(ctx, dst, src);
 		}
 		break;
 	/* dst = -dst */
@@ -1555,8 +1570,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
 		break;
 	/* dst = src (64-bit) */
 	case BPF_ALU64 | BPF_MOV | BPF_X:
-		emit_mov_r(ctx, lo(dst), lo(src));
-		emit_mov_r(ctx, hi(dst), hi(src));
+		emit_mov_r64(ctx, dst, src);
 		break;
 	/* dst = -dst (64-bit) */
 	case BPF_ALU64 | BPF_NEG:
diff --git a/arch/mips/net/bpf_jit_comp64.c b/arch/mips/net/bpf_jit_comp64.c
index fa7e9aa37f49..45fee6f6b87e 100644
--- a/arch/mips/net/bpf_jit_comp64.c
+++ b/arch/mips/net/bpf_jit_comp64.c
@@ -120,6 +120,19 @@ static void emit_zext_ver(struct jit_context *ctx, u8 dst)
 		emit_zext(ctx, dst);
 }
 
+/* Register move operation (32-bit) */
+static void emit_mov_r32(struct jit_context *ctx, u8 dst, u8 src)
+{
+	emit_mov_r(ctx, dst, src);
+	emit_zext_ver(ctx, dst);
+}
+
+/* Register move operation (64-bit) */
+static void emit_mov_r64(struct jit_context *ctx, u8 dst, u8 src)
+{
+	emit_mov_r(ctx, dst, src);
+}
+
 /* dst = imm (64-bit) */
 static void emit_mov_i64(struct jit_context *ctx, u8 dst, u64 imm64)
 {
@@ -656,8 +669,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
 			/* Special mov32 for zext */
 			emit_zext(ctx, dst);
 		} else {
-			emit_mov_r(ctx, dst, src);
-			emit_zext_ver(ctx, dst);
+			emit_mov_r32(ctx, dst, src);
 		}
 		break;
 	/* dst = -dst */
@@ -742,7 +754,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
 		break;
 	/* dst = src (64-bit) */
 	case BPF_ALU64 | BPF_MOV | BPF_X:
-		emit_mov_r(ctx, dst, src);
+		emit_mov_r64(ctx, dst, src);
 		break;
 	/* dst = -dst (64-bit) */
 	case BPF_ALU64 | BPF_NEG:

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH bpf-next v2 2/2] bpf, mips: Add support for BPF_MOVSX in the JITs
  2026-09-04 11:17 [PATCH bpf-next v2 0/2] bpf, mips: Add BPF_MOVSX support to the JITs Nicholas Dudar
  2026-09-04 11:17 ` [PATCH bpf-next v2 1/2] bpf, mips: Factor register moves into helpers Nicholas Dudar
@ 2026-09-04 11:17 ` Nicholas Dudar
  1 sibling, 0 replies; 5+ messages in thread
From: Nicholas Dudar @ 2026-09-04 11:17 UTC (permalink / raw)
  To: ast, daniel, andrii, eddyz87, memxor, johan.almbladh, paulburton,
	tsbogend
  Cc: martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai, bpf,
	linux-mips, linux-kernel

Both MIPS JITs ignore insn->off when lowering register MOV
instructions, so BPF_MOVSX is emitted as an ordinary move. Since
build_insn() accepts it, affected programs are silently miscompiled
instead of falling back to the interpreter.

Decode insn->off in the register-move helpers and sign-extend 8- and
16-bit ALU32 operands and 8-, 16-, and 32-bit ALU64 operands. On
MIPS32, propagate the sign into the high word for ALU64 while
preserving the existing ALU32 zero-extension handling.

Use shift pairs rather than seb/seh so the lowering works on every CPU
supported by these JITs. seb/seh would save one instruction on
R2-or-newer CPUs but would require adding those opcodes to uasm.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Nicholas Dudar <main.kalliope@gmail.com>
---
 arch/mips/net/bpf_jit_comp32.c | 56 ++++++++++++++++++++++++++++------
 arch/mips/net/bpf_jit_comp64.c | 54 +++++++++++++++++++++++++++-----
 2 files changed, 93 insertions(+), 17 deletions(-)

diff --git a/arch/mips/net/bpf_jit_comp32.c b/arch/mips/net/bpf_jit_comp32.c
index bfe73b023983..885da0d0d74b 100644
--- a/arch/mips/net/bpf_jit_comp32.c
+++ b/arch/mips/net/bpf_jit_comp32.c
@@ -190,20 +190,58 @@ static void emit_zext_ver(struct jit_context *ctx, const u8 dst[])
 	}
 }
 
-/* Register move operation (32-bit) */
+/* Register move operation (32-bit), optionally with sign extension */
 static void emit_mov_r32(struct jit_context *ctx, const u8 dst[],
-			 const u8 src[])
+			 const u8 src[], s16 off)
 {
-	emit_mov_r(ctx, lo(dst), lo(src));
+	int shift;
+
+	switch (off) {
+	case 8:
+	case 16:
+		shift = 32 - off;
+		emit(ctx, sll, lo(dst), lo(src), shift);
+		emit(ctx, sra, lo(dst), lo(dst), shift);
+		break;
+	default:
+		/* off == 0 is MOV; the verifier rejects other offsets. */
+		emit_mov_r(ctx, lo(dst), lo(src));
+		break;
+	}
+	clobber_reg(ctx, lo(dst));
 	emit_zext_ver(ctx, dst);
 }
 
-/* Register move operation (64-bit) */
+/* Register move operation (64-bit), optionally with sign extension */
 static void emit_mov_r64(struct jit_context *ctx, const u8 dst[],
-			 const u8 src[])
+			 const u8 src[], s16 off)
 {
-	emit_mov_r(ctx, lo(dst), lo(src));
-	emit_mov_r(ctx, hi(dst), hi(src));
+	int shift;
+
+	switch (off) {
+	case 8:
+	case 16:
+		shift = 32 - off;
+		emit(ctx, sll, lo(dst), lo(src), shift);
+		emit(ctx, sra, lo(dst), lo(dst), shift);
+		emit(ctx, sra, hi(dst), lo(dst), 31);
+		break;
+	case 32:
+		emit(ctx, move, lo(dst), lo(src));
+		emit(ctx, sra, hi(dst), lo(dst), 31);
+		break;
+	default:
+		/*
+		 * off == 0 is ordinary MOV. The verifier rejects other
+		 * offsets; defined exceptions require
+		 * bpf_jit_supports_percpu_insn() or bpf_jit_supports_arena(),
+		 * neither implemented by MIPS.
+		 */
+		emit_mov_r(ctx, lo(dst), lo(src));
+		emit_mov_r(ctx, hi(dst), hi(src));
+		break;
+	}
+	clobber_reg64(ctx, dst);
 }
 
 /* Load delay slot, if ISA mandates it */
@@ -1501,7 +1539,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
 			/* Special mov32 for zext */
 			emit_mov_i(ctx, hi(dst), 0);
 		} else {
-			emit_mov_r32(ctx, dst, src);
+			emit_mov_r32(ctx, dst, src, off);
 		}
 		break;
 	/* dst = -dst */
@@ -1570,7 +1608,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
 		break;
 	/* dst = src (64-bit) */
 	case BPF_ALU64 | BPF_MOV | BPF_X:
-		emit_mov_r64(ctx, dst, src);
+		emit_mov_r64(ctx, dst, src, off);
 		break;
 	/* dst = -dst (64-bit) */
 	case BPF_ALU64 | BPF_NEG:
diff --git a/arch/mips/net/bpf_jit_comp64.c b/arch/mips/net/bpf_jit_comp64.c
index 45fee6f6b87e..a1dfb7492a9c 100644
--- a/arch/mips/net/bpf_jit_comp64.c
+++ b/arch/mips/net/bpf_jit_comp64.c
@@ -120,17 +120,55 @@ static void emit_zext_ver(struct jit_context *ctx, u8 dst)
 		emit_zext(ctx, dst);
 }
 
-/* Register move operation (32-bit) */
-static void emit_mov_r32(struct jit_context *ctx, u8 dst, u8 src)
+/* Register move operation (32-bit), optionally with sign extension */
+static void emit_mov_r32(struct jit_context *ctx, u8 dst, u8 src, s16 off)
 {
-	emit_mov_r(ctx, dst, src);
+	int shift;
+
+	switch (off) {
+	case 8:
+	case 16:
+		shift = 32 - off;
+		/* dsll32 and dsra32 add 32 to the shift argument. */
+		emit(ctx, dsll32, dst, src, shift);
+		emit(ctx, dsra32, dst, dst, shift);
+		break;
+	default:
+		/* off == 0 is MOV; the verifier rejects other offsets. */
+		emit_mov_r(ctx, dst, src);
+		break;
+	}
+	clobber_reg(ctx, dst);
 	emit_zext_ver(ctx, dst);
 }
 
-/* Register move operation (64-bit) */
-static void emit_mov_r64(struct jit_context *ctx, u8 dst, u8 src)
+/* Register move operation (64-bit), optionally with sign extension */
+static void emit_mov_r64(struct jit_context *ctx, u8 dst, u8 src, s16 off)
 {
-	emit_mov_r(ctx, dst, src);
+	int shift;
+
+	switch (off) {
+	case 8:
+	case 16:
+		shift = 32 - off;
+		/* dsll32 and dsra32 add 32 to the shift argument. */
+		emit(ctx, dsll32, dst, src, shift);
+		emit(ctx, dsra32, dst, dst, shift);
+		break;
+	case 32:
+		emit_sext(ctx, dst, src);
+		break;
+	default:
+		/*
+		 * off == 0 is ordinary MOV. The verifier rejects other
+		 * offsets; defined exceptions require
+		 * bpf_jit_supports_percpu_insn() or bpf_jit_supports_arena(),
+		 * neither implemented by MIPS.
+		 */
+		emit_mov_r(ctx, dst, src);
+		break;
+	}
+	clobber_reg(ctx, dst);
 }
 
 /* dst = imm (64-bit) */
@@ -669,7 +707,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
 			/* Special mov32 for zext */
 			emit_zext(ctx, dst);
 		} else {
-			emit_mov_r32(ctx, dst, src);
+			emit_mov_r32(ctx, dst, src, off);
 		}
 		break;
 	/* dst = -dst */
@@ -754,7 +792,7 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
 		break;
 	/* dst = src (64-bit) */
 	case BPF_ALU64 | BPF_MOV | BPF_X:
-		emit_mov_r64(ctx, dst, src);
+		emit_mov_r64(ctx, dst, src, off);
 		break;
 	/* dst = -dst (64-bit) */
 	case BPF_ALU64 | BPF_NEG:

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next v2 1/2] bpf, mips: Factor register moves into helpers
  2026-09-04 11:17 ` [PATCH bpf-next v2 1/2] bpf, mips: Factor register moves into helpers Nicholas Dudar
@ 2026-09-04 12:29   ` bot+bpf-ci
  2026-09-04 12:45     ` Nicholas Dudar
  0 siblings, 1 reply; 5+ messages in thread
From: bot+bpf-ci @ 2026-09-04 12:29 UTC (permalink / raw)
  To: main.kalliope, ast, daniel, andrii, eddyz87, memxor,
	johan.almbladh, paulburton, tsbogend
  Cc: martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai, bpf,
	linux-mips, linux-kernel, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, mason, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1461 bytes --]

> BPF_MOVSX carries its source width in insn->off. Both MIPS JITs
> currently lower register MOV instructions directly in build_insn().
> Keeping the forthcoming MOVSX width switch there would split the move
> operation between dispatcher cases and backend emitters.
>
> Factor ALU32 and ALU64 register moves into helpers for each backend and
> route the existing MOV paths through them. The helpers own the complete
> move operation, including MIPS32 register-pair handling and the ALU32
> upper-half state. This keeps the following MOVSX width decoding inside
> one per-backend, per-width-class move emitter rather than in dispatcher
> case arms or generic primitives.

This isn't a bug, but the second paragraph seems to repeat the rationale
already given in the first. The first paragraph explains that keeping the
MOVSX width switch in build_insn() would split the move operation between
dispatcher cases and backend emitters. The second paragraph restates this
as keeping MOVSX width decoding inside one per-backend, per-width-class
move emitter rather than in dispatcher case arms or generic primitives.

Could the two be collapsed into a single sentence about why the move
lowering needs to live in one emitter?


---
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/33869345053

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next v2 1/2] bpf, mips: Factor register moves into helpers
  2026-09-04 12:29   ` bot+bpf-ci
@ 2026-09-04 12:45     ` Nicholas Dudar
  0 siblings, 0 replies; 5+ messages in thread
From: Nicholas Dudar @ 2026-09-04 12:45 UTC (permalink / raw)
  To: bot+bpf-ci
  Cc: ast, daniel, andrii, eddyz87, memxor, johan.almbladh, paulburton,
	tsbogend, martin.lau, song, yonghong.song, jolsa, emil,
	ihor.solodrai, bpf, linux-mips, linux-kernel, martin.lau, mason

> Could the two be collapsed into a single sentence about why the move
> lowering needs to live in one emitter?

The first paragraph describes the problem with adding width handling in
build_insn(), while the second describes the refactor and the state owned
by the helpers. I think that distinction is useful, so I prefer to keep
the current wording.

Nicholas

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-04 12:45 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 11:17 [PATCH bpf-next v2 0/2] bpf, mips: Add BPF_MOVSX support to the JITs Nicholas Dudar
2026-09-04 11:17 ` [PATCH bpf-next v2 1/2] bpf, mips: Factor register moves into helpers Nicholas Dudar
2026-09-04 12:29   ` bot+bpf-ci
2026-09-04 12:45     ` Nicholas Dudar
2026-09-04 11:17 ` [PATCH bpf-next v2 2/2] bpf, mips: Add support for BPF_MOVSX in the JITs Nicholas Dudar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox