Re: [PATCH bpf-next v4 2/4] riscv, bpf: add RV32G eBPF JIT

From: BjÃrn TÃpel
Date: Wed Mar 04 2020 - 00:59:31 EST


On Wed, 4 Mar 2020 at 03:32, Luke Nelson <lukenels@xxxxxxxxxxxxxxxxx> wrote:
>
[...]
>
> > > + case BPF_LSH:
> > > + if (imm >= 32) {
> > > + emit(rv_slli(hi(rd), lo(rd), imm - 32), ctx);
> > > + emit(rv_addi(lo(rd), RV_REG_ZERO, 0), ctx);
> > > + } else if (imm == 0) {
> > > + /* nop */
> >
> > Can we get rid of this, and just do if/else if?
>
> imm == 0 has been a tricky case for 32-bit JITs; see 6fa632e719ee
> ("bpf, x32: Fix bug with ALU64 {LSH, RSH, ARSH} BPF_K shift by 0").
> We wanted to make the imm == 0 case explicit and help future readers
> see that this case is handled correctly here.
>
> We could do the following if we really wanted to get rid of the
> check:
>
> if (imm >= 32) {
> ...
> } else if (imm != 0) {
> ...
> }
> /* Do nothing for imm == 0. */
>
> Though it's unclear if this is easier to read.
>

Thanks for clearing that up. *I* prefer the latter, but that's really
up to you! Keep the current one, if you prefer that! :-)

> > > + case BPF_ARSH:
> > > + if (is_12b_int(imm)) {
> > > + emit(rv_srai(lo(rd), lo(rd), imm), ctx);
> > > + } else {
> > > + emit_imm(RV_REG_T0, imm, ctx);
> > > + emit(rv_sra(lo(rd), lo(rd), RV_REG_T0), ctx);
> > > + }
> > > + break;
> >
> > Again nit; I like "early exit" code if possible. Instead of:
> >
> > if (bleh) {
> > foo();
> > } else {
> > bar();
> > }
> >
> > do:
> >
> > if (bleh) {
> > foo()
> > return/break;
> > }
> > bar();
> >
> > I find the latter easier to read -- but really a nit, and a matter of
> > style. There are number of places where that could be applied in the
> > file.
>
> I like "early exit" code, too, and agree that it's easier to read
> in general, especially when handling error conditions.
>
> But here we wanted to make it explicit that both branches are
> emitting equivalent instruction sequences (under different paths).
> Structured control flow seems a better fit for this particular
> context.
>

Ok!

> > At this point of the series, let's introduce the shared code .c-file
> > containing implementation for bpf_int_jit_compile() (with build_body
> > part of that)and bpf_jit_needs_zext(). That will make it easier to
> > catch bugs in both JITs and to avoid code duplication! Also, when
> > adding the stronger invariant suggested by Palmer [1], we only need to
> > do it in one place.
> >
> > The pull out refactoring can be a separate commit.
>
> I think the idea of deduplicating bpf_int_jit_compile is good and
> will lead to more maintainable JITs. How does the following proposal
> for v5 sound?
>
> In patch 1 of this series:
>
> - Factor structs and common helpers to bpf_jit.h (just like v4).
>
> - Factor out bpf_int_jit_compile(), bpf_jit_needs_zext(), and
> build_body() to a new file bpf_jit_core.c and tweak the code as in v4.
>
> - Rename emit_insn() and build_{prologue,epilogue}() to bpf_jit_emit_insn()
> and bpf_jit_build_{prologue,epilogue}, since these functions are
> now extern rather than static.
>
> - Rename bpf_jit_comp.c to bpf_jit_comp64.c to be more explicit
> about its contents (as the next patch will add bpf_jit_comp32.c).
>
> Then patch 2 can reuse the new header and won't need to define its
> own bpf_int_jit_compile() etc.
>

I like that, but keep the first patch as a refactoring patch only, and
then in a *new* patch 2 you add the rv32 specific code (sltu and
pseudo instructions + the xlen preprocessor check + copyright-things
;-)). Patch 3 will be the old patch 2. Wdyt?

Thanks for working on this!
BjÃrn

> Thanks!
>
> Luke