[RFC PATCH bpf-next 2/3] bpf, riscv: Add encodings for compressed instructions

Björn Töpel bjorn.topel at gmail.com
Wed Jul 15 14:10:06 EDT 2020


On Mon, 13 Jul 2020 at 20:37, Luke Nelson <lukenels at cs.washington.edu> wrote:
>
> This patch adds functions for encoding and emitting compressed riscv
> (RVC) instructions to the BPF JIT.
>
> Some regular riscv instructions can be compressed into an RVC instruction
> if the instruction fields meet some requirements. For example, "add rd,
> rs1, rs2" can be compressed into "c.add rd, rs2" when rd == rs1.
>
> To make using RVC encodings simpler, this patch also adds helper
> functions that selectively emit either a regular instruction or a
> compressed instruction if possible.
>
> For example, emit_add will produce a "c.add" if possible and regular
> "add" otherwise.
>
> Signed-off-by: Luke Nelson <luke.r.nels at gmail.com>
> ---
>  arch/riscv/net/bpf_jit.h | 474 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 474 insertions(+)
>
> diff --git a/arch/riscv/net/bpf_jit.h b/arch/riscv/net/bpf_jit.h
> index 5c89ea904c1a..f3ac2d4a50f7 100644
> --- a/arch/riscv/net/bpf_jit.h
> +++ b/arch/riscv/net/bpf_jit.h
> @@ -13,6 +13,15 @@
>  #include <linux/filter.h>
>  #include <asm/cacheflush.h>
>
> +static inline bool rvc_enabled(void)
> +{
> +#ifdef CONFIG_RISCV_ISA_C
> +       return true;
> +#else
> +       return false;
> +#endif
> +}
> +
>  enum {
>         RV_REG_ZERO =   0,      /* The constant value 0 */
>         RV_REG_RA =     1,      /* Return address */
> @@ -48,6 +57,18 @@ enum {
>         RV_REG_T6 =     31,
>  };
>
> +static inline bool is_creg(u8 reg)
> +{
> +       return (1 << reg) & (BIT(RV_REG_FP) |
> +                            BIT(RV_REG_S1) |
> +                            BIT(RV_REG_A0) |
> +                            BIT(RV_REG_A1) |
> +                            BIT(RV_REG_A2) |
> +                            BIT(RV_REG_A3) |
> +                            BIT(RV_REG_A4) |
> +                            BIT(RV_REG_A5));
> +}
> +
>  struct rv_jit_context {
>         struct bpf_prog *prog;
>         u16 *insns;             /* RV insns */
> @@ -134,6 +155,16 @@ static inline int invert_bpf_cond(u8 cond)
>         return -1;
>  }
>
> +static inline bool is_6b_int(long val)
> +{
> +       return -(1L << 5) <= val && val < (1L << 5);
> +}
> +
> +static inline bool is_10b_int(long val)
> +{
> +       return -(1L << 9) <= val && val < (1L << 9);
> +}
> +
>  static inline bool is_12b_int(long val)
>  {
>         return -(1L << 11) <= val && val < (1L << 11);
> @@ -224,6 +255,59 @@ static inline u32 rv_amo_insn(u8 funct5, u8 aq, u8 rl, u8 rs2, u8 rs1,
>         return rv_r_insn(funct7, rs2, rs1, funct3, rd, opcode);
>  }
>
> +/* RISC-V compressed instruction formats. */
> +
> +static inline u32 rv_cr_insn(u8 funct4, u8 rd, u8 rs2, u8 op)

Please change so that the return type is u16, so it matches emitc.

> +{
> +       return (funct4 << 12) | (rd << 7) | (rs2 << 2) | op;
> +}
> +
> +static inline u32 rv_ci_insn(u8 funct3, u32 imm6, u8 rd, u8 op)
> +{
> +       u16 imm;
> +
> +       imm = ((imm6 & 0x20) << 7) | ((imm6 & 0x1f) << 2);
> +       return (funct3 << 13) | (rd << 7) | op | imm;
> +}
> +
> +static inline u32 rv_css_insn(u8 funct3, u32 uimm, u8 rs2, u8 op)
> +{
> +       return (funct3 << 13) | (uimm << 7) | (rs2 << 2) | op;
> +}
> +
> +static inline u32 rv_ciw_insn(u8 funct3, u32 uimm, u8 rd, u8 op)
> +{
> +       return (funct3 << 13) | (uimm << 5) | ((rd & 0x7) << 2) | op;
> +}
> +
> +static inline u32 rv_cl_insn(u8 funct3, u32 imm_hi, u8 rs1, u32 imm_lo, u8 rd,
> +                            u8 op)
> +{
> +       return (funct3 << 13) | (imm_hi << 10) | ((rs1 & 0x7) << 7) |
> +               (imm_lo << 5) | ((rd & 0x7) << 2) | op;
> +}
> +
> +static inline u32 rv_cs_insn(u8 funct3, u32 imm_hi, u8 rs1, u32 imm_lo, u8 rs2,
> +                            u8 op)
> +{
> +       return (funct3 << 13) | (imm_hi << 10) | ((rs1 & 0x7) << 7) |
> +               (imm_lo << 5) | ((rs2 & 0x7) << 2) | op;
> +}
> +
> +static inline u32 rv_ca_insn(u8 funct6, u8 rd, u8 funct2, u8 rs2, u8 op)
> +{
> +       return (funct6 << 10) | ((rd & 0x7) << 7) | (funct2 << 5) |
> +               ((rs2 & 0x7) << 2) | op;
> +}
> +
> +static inline u32 rv_cb_insn(u8 funct3, u32 imm6, u8 funct2, u8 rd, u8 op)
> +{
> +       u16 imm;
> +
> +       imm = ((imm6 & 0x20) << 7) | ((imm6 & 0x1f) << 2);
> +       return (funct3 << 13) | (funct2 << 10) | ((rd & 0x7) << 7) | op | imm;
> +}
> +
>  /* Instructions shared by both RV32 and RV64. */
>
>  static inline u32 rv_addi(u8 rd, u8 rs1, u16 imm11_0)
> @@ -431,6 +515,135 @@ static inline u32 rv_amoadd_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
>         return rv_amo_insn(0, aq, rl, rs2, rs1, 2, rd, 0x2f);
>  }
>
> +/* RVC instrutions. */
> +
> +static inline u32 rvc_addi4spn(u8 rd, u32 imm10)

And same here. Change to u16 for the return value.

> +{
> +       u32 imm;
> +
> +       imm = ((imm10 & 0x30) << 2) | ((imm10 & 0x3c0) >> 4) |
> +               ((imm10 & 0x4) >> 1) | ((imm10 & 0x8) >> 3);
> +       return rv_ciw_insn(0x0, imm, rd, 0x0);
> +}
> +
> +static inline u32 rvc_lw(u8 rd, u32 imm7, u8 rs1)
> +{
> +       u32 imm_hi, imm_lo;
> +
> +       imm_hi = (imm7 & 0x38) >> 3;
> +       imm_lo = ((imm7 & 0x4) >> 1) | ((imm7 & 0x40) >> 6);
> +       return rv_cl_insn(0x2, imm_hi, rs1, imm_lo, rd, 0x0);
> +}
> +
> +static inline u32 rvc_sw(u8 rs1, u32 imm7, u8 rs2)
> +{
> +       u32 imm_hi, imm_lo;
> +
> +       imm_hi = (imm7 & 0x38) >> 3;
> +       imm_lo = ((imm7 & 0x4) >> 1) | ((imm7 & 0x40) >> 6);
> +       return rv_cs_insn(0x6, imm_hi, rs1, imm_lo, rs2, 0x0);
> +}
> +
> +static inline u32 rvc_addi(u8 rd, u8 imm6)
> +{
> +       return rv_ci_insn(0, imm6, rd, 0x1);
> +}
> +
> +static inline u32 rvc_li(u8 rd, u8 imm6)
> +{
> +       return rv_ci_insn(0x2, imm6, rd, 0x1);
> +}
> +
> +static inline u32 rvc_addi16sp(u32 imm10)
> +{
> +       u32 imm;
> +
> +       imm = ((imm10 & 0x200) >> 4) | (imm10 & 0x10) | ((imm10 & 0x40) >> 3) |
> +               ((imm10 & 0x180) >> 6) | ((imm10 & 0x20) >> 5);
> +       return rv_ci_insn(0x3, imm, RV_REG_SP, 0x1);
> +}
> +
> +static inline u32 rvc_lui(u8 rd, u8 imm6)
> +{
> +       return rv_ci_insn(0x3, imm6, rd, 0x1);
> +}
> +
> +static inline u32 rvc_srli(u8 rd, u8 imm6)
> +{
> +       return rv_cb_insn(0x4, imm6, 0, rd, 0x1);
> +}
> +
> +static inline u32 rvc_srai(u8 rd, u8 imm6)
> +{
> +       return rv_cb_insn(0x4, imm6, 0x1, rd, 0x1);
> +}
> +
> +static inline u32 rvc_andi(u8 rd, u8 imm6)
> +{
> +       return rv_cb_insn(0x4, imm6, 0x2, rd, 0x1);
> +}
> +
> +static inline u32 rvc_sub(u8 rd, u8 rs)
> +{
> +       return rv_ca_insn(0x23, rd, 0, rs, 0x1);
> +}
> +
> +static inline u32 rvc_xor(u8 rd, u8 rs)
> +{
> +       return rv_ca_insn(0x23, rd, 0x1, rs, 0x1);
> +}
> +
> +static inline u32 rvc_or(u8 rd, u8 rs)
> +{
> +       return rv_ca_insn(0x23, rd, 0x2, rs, 0x1);
> +}
> +
> +static inline u32 rvc_and(u8 rd, u8 rs)
> +{
> +       return rv_ca_insn(0x23, rd, 0x3, rs, 0x1);
> +}
> +
> +static inline u32 rvc_slli(u8 rd, u8 imm6)
> +{
> +       return rv_ci_insn(0, imm6, rd, 0x2);
> +}
> +
> +static inline u32 rvc_lwsp(u8 rd, u32 imm8)
> +{
> +       u32 imm;
> +
> +       imm = ((imm8 & 0xc0) >> 6) | (imm8 & 0x3c);
> +       return rv_ci_insn(0x2, imm, rd, 0x2);
> +}
> +
> +static inline u32 rvc_jr(u8 rs1)
> +{
> +       return rv_cr_insn(0x8, rs1, RV_REG_ZERO, 0x2);
> +}
> +
> +static inline u32 rvc_mv(u8 rd, u8 rs)
> +{
> +       return rv_cr_insn(0x8, rd, rs, 0x2);
> +}
> +
> +static inline u32 rvc_jalr(u8 rs1)
> +{
> +       return rv_cr_insn(0x9, rs1, RV_REG_ZERO, 0x2);
> +}
> +
> +static inline u32 rvc_add(u8 rd, u8 rs)
> +{
> +       return rv_cr_insn(0x9, rd, rs, 0x2);
> +}
> +
> +static inline u32 rvc_swsp(u32 imm8, u8 rs2)
> +{
> +       u32 imm;
> +
> +       imm = (imm8 & 0x3c) | ((imm8 & 0xc0) >> 6);
> +       return rv_css_insn(0x6, imm, rs2, 0x2);
> +}
> +
>  /*
>   * RV64-only instructions.
>   *
> @@ -520,6 +733,267 @@ static inline u32 rv_amoadd_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
>         return rv_amo_insn(0, aq, rl, rs2, rs1, 3, rd, 0x2f);
>  }
>
> +/* RV64-only RVC instructions. */
> +
> +static inline u32 rvc_ld(u8 rd, u32 imm8, u8 rs1)

...and again also u16 return type.

> +{
> +       u32 imm_hi, imm_lo;
> +
> +       imm_hi = (imm8 & 0x38) >> 3;
> +       imm_lo = (imm8 & 0xc0) >> 6;
> +       return rv_cl_insn(0x3, imm_hi, rs1, imm_lo, rd, 0x0);
> +}
> +
> +static inline u32 rvc_sd(u8 rs1, u32 imm8, u8 rs2)
> +{
> +       u32 imm_hi, imm_lo;
> +
> +       imm_hi = (imm8 & 0x38) >> 3;
> +       imm_lo = (imm8 & 0xc0) >> 6;
> +       return rv_cs_insn(0x7, imm_hi, rs1, imm_lo, rs2, 0x0);
> +}
> +
> +static inline u32 rvc_subw(u8 rd, u8 rs)
> +{
> +       return rv_ca_insn(0x27, rd, 0, rs, 0x1);
> +}
> +
> +static inline u32 rvc_addiw(u8 rd, u8 imm6)
> +{
> +       return rv_ci_insn(0x1, imm6, rd, 0x1);
> +}
> +
> +static inline u32 rvc_ldsp(u8 rd, u32 imm9)
> +{
> +       u32 imm;
> +
> +       imm = ((imm9 & 0x1c0) >> 6) | (imm9 & 0x38);
> +       return rv_ci_insn(0x3, imm, rd, 0x2);
> +}
> +
> +static inline u32 rvc_sdsp(u32 imm9, u8 rs2)
> +{
> +       u32 imm;
> +
> +       imm = (imm9 & 0x38) | ((imm9 & 0x1c0) >> 6);
> +       return rv_css_insn(0x7, imm, rs2, 0x2);
> +}
> +
> +#endif /* __riscv_xlen == 64 */
> +
> +/* Helper functions that emit RVC instructions when possible. */
> +
> +static inline void emit_jalr(u8 rd, u8 rs, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rd == RV_REG_RA && rs && !imm)
> +               emitc(rvc_jalr(rs), ctx);
> +       else if (rvc_enabled() && !rd && rs && !imm)
> +               emitc(rvc_jr(rs), ctx);
> +       else
> +               emit(rv_jalr(rd, rs, imm), ctx);
> +}
> +
> +static inline void emit_mv(u8 rd, u8 rs, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rd && rs)
> +               emitc(rvc_mv(rd, rs), ctx);
> +       else
> +               emit(rv_addi(rd, rs, 0), ctx);
> +}
> +
> +static inline void emit_add(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rd && rd == rs1 && rs2)
> +               emitc(rvc_add(rd, rs2), ctx);
> +       else
> +               emit(rv_add(rd, rs1, rs2), ctx);
> +}
> +
> +static inline void emit_addi(u8 rd, u8 rs, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rd == rs && !imm)
> +               /*
> +                * RVC cannot handle imm == 0. Handle it here by emitting
> +                * no instructions since it should behave as a no-op.
> +                */
> +               return;
> +       else if (rvc_enabled() && rd == RV_REG_SP && rd == rs &&
> +                is_10b_int(imm) && !(imm & 0xf))
> +               emitc(rvc_addi16sp(imm), ctx);
> +       else if (rvc_enabled() && is_creg(rd) && rs == RV_REG_SP &&
> +                (u32)imm < 0x400 && !(imm & 0x3) && imm)
> +               emitc(rvc_addi4spn(rd, imm), ctx);
> +       else if (rvc_enabled() && rd && rd == rs && is_6b_int(imm))
> +               emitc(rvc_addi(rd, imm), ctx);
> +       else
> +               emit(rv_addi(rd, rs, imm), ctx);
> +}
> +
> +static inline void emit_li(u8 rd, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rd && is_6b_int(imm))
> +               emitc(rvc_li(rd, imm), ctx);
> +       else
> +               emit(rv_addi(rd, RV_REG_ZERO, imm), ctx);
> +}
> +
> +static inline void emit_lui(u8 rd, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rd && rd != RV_REG_SP && is_6b_int(imm) && imm)
> +               emitc(rvc_lui(rd, imm), ctx);
> +       else
> +               emit(rv_lui(rd, imm), ctx);
> +}
> +
> +static inline void emit_slli(u8 rd, u8 rs, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rd == rs && !imm)
> +               /*
> +                * RVC cannot handle imm == 0. Handle it here by emitting
> +                * no instructions since it should behave as a no-op.
> +                */
> +               return;
> +       else if (rvc_enabled() && rd && rd == rs)
> +               emitc(rvc_slli(rd, imm), ctx);
> +       else
> +               emit(rv_slli(rd, rs, imm), ctx);
> +}
> +
> +static inline void emit_andi(u8 rd, u8 rs, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && is_creg(rd) && rd == rs && is_6b_int(imm))
> +               emitc(rvc_andi(rd, imm), ctx);
> +       else
> +               emit(rv_andi(rd, rs, imm), ctx);
> +}
> +
> +static inline void emit_srli(u8 rd, u8 rs, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rd == rs && !imm)
> +               /*
> +                * RVC cannot handle imm == 0. Handle it here by emitting
> +                * no instructions since it should behave as a no-op.
> +                */
> +               return;
> +       else if (rvc_enabled() && is_creg(rd) && rd == rs)
> +               emitc(rvc_srli(rd, imm), ctx);
> +       else
> +               emit(rv_srli(rd, rs, imm), ctx);
> +}
> +
> +static inline void emit_srai(u8 rd, u8 rs, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rd == rs && !imm)
> +               /*
> +                * RVC cannot handle imm == 0. Handle it here by emitting
> +                * no instructions since it should behave as a no-op.
> +                */
> +               return;
> +       else if (rvc_enabled() && is_creg(rd) && rd == rs)
> +               emitc(rvc_srai(rd, imm), ctx);
> +       else
> +               emit(rv_srai(rd, rs, imm), ctx);
> +}
> +
> +static inline void emit_sub(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && is_creg(rd) && rd == rs1 && is_creg(rs2))
> +               emitc(rvc_sub(rd, rs2), ctx);
> +       else
> +               emit(rv_sub(rd, rs1, rs2), ctx);
> +}
> +
> +static inline void emit_or(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && is_creg(rd) && rd == rs1 && is_creg(rs2))
> +               emitc(rvc_or(rd, rs2), ctx);
> +       else
> +               emit(rv_or(rd, rs1, rs2), ctx);
> +}
> +
> +static inline void emit_and(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && is_creg(rd) && rd == rs1 && is_creg(rs2))
> +               emitc(rvc_and(rd, rs2), ctx);
> +       else
> +               emit(rv_and(rd, rs1, rs2), ctx);
> +}
> +
> +static inline void emit_xor(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && is_creg(rd) && rd == rs1 && is_creg(rs2))
> +               emitc(rvc_xor(rd, rs2), ctx);
> +       else
> +               emit(rv_xor(rd, rs1, rs2), ctx);
> +}
> +
> +static inline void emit_lw(u8 rd, s32 off, u8 rs1, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rs1 == RV_REG_SP && rd && (u32)off < 0x100 &&
> +           !(off & 0x3))
> +               emitc(rvc_lwsp(rd, off), ctx);
> +       else if (rvc_enabled() && is_creg(rd) && is_creg(rs1) &&
> +                (u32)off < 0x80 && !(off & 0x3))
> +               emitc(rvc_lw(rd, off, rs1), ctx);
> +       else
> +               emit(rv_lw(rd, off, rs1), ctx);
> +}
> +
> +static inline void emit_sw(u8 rs1, s32 off, u8 rs2, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rs1 == RV_REG_SP && (u32)off < 0x100 &&
> +           !(off & 0x3))
> +               emitc(rvc_swsp(off, rs2), ctx);
> +       else if (rvc_enabled() && is_creg(rs1) && is_creg(rs2) &&
> +                (u32)off < 0x80 && !(off & 0x3))
> +               emitc(rvc_sw(rs1, off, rs2), ctx);
> +       else
> +               emit(rv_sw(rs1, off, rs2), ctx);
> +}
> +
> +/* RV64-only helper functions. */
> +#if __riscv_xlen == 64
> +
> +static inline void emit_addiw(u8 rd, u8 rs, s32 imm, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rd && rd == rs && is_6b_int(imm))
> +               emitc(rvc_addiw(rd, imm), ctx);
> +       else
> +               emit(rv_addiw(rd, rs, imm), ctx);
> +}
> +
> +static inline void emit_ld(u8 rd, s32 off, u8 rs1, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rs1 == RV_REG_SP && rd && (u32)off < 0x200 &&
> +           !(off & 0x7))
> +               emitc(rvc_ldsp(rd, off), ctx);
> +       else if (rvc_enabled() && is_creg(rd) && is_creg(rs1) &&
> +                (u32)off < 0x100 && !(off & 0x7))
> +               emitc(rvc_ld(rd, off, rs1), ctx);
> +       else
> +               emit(rv_ld(rd, off, rs1), ctx);
> +}
> +
> +static inline void emit_sd(u8 rs1, s32 off, u8 rs2, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && rs1 == RV_REG_SP && (u32)off < 0x200 &&
> +           !(off & 0x7))
> +               emitc(rvc_sdsp(off, rs2), ctx);
> +       else if (rvc_enabled() && is_creg(rs1) && is_creg(rs2) &&
> +                (u32)off < 0x100 && !(off & 0x7))
> +               emitc(rvc_sd(rs1, off, rs2), ctx);
> +       else
> +               emit(rv_sd(rs1, off, rs2), ctx);
> +}
> +
> +static inline void emit_subw(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
> +{
> +       if (rvc_enabled() && is_creg(rd) && rd == rs1 && is_creg(rs2))
> +               emitc(rvc_subw(rd, rs2), ctx);
> +       else
> +               emit(rv_subw(rd, rs1, rs2), ctx);
> +}
> +
>  #endif /* __riscv_xlen == 64 */
>
>  void bpf_jit_build_prologue(struct rv_jit_context *ctx);
> --
> 2.25.1
>



More information about the linux-riscv mailing list