nfp: bpf: support arithmetic indirect right shift (BPF_ARSH | BPF_X)
authorJiong Wang <jiong.wang@netronome.com>
Fri, 18 May 2018 19:12:11 +0000 (12:12 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Fri, 18 May 2018 19:35:55 +0000 (21:35 +0200)
Code logic is similar with arithmetic right shift by constant, and NFP
get indirect shift amount through source A operand of PREV_ALU.

It is possible to fall back to logic right shift if the MSB is known to be
zero from range info, however there is no benefit to do this given logic
indirect right shift use the same number and cycle of instruction sequence.

Suppose the MSB of regX is the bit we want to replicate to fill in all the
vacant positions, and regY contains the shift amount, then we could use
single instruction to set up both.

  [alu, --, regY, OR, regX]

  --
  NOTE: the PREV_ALU result doesn't need to write to any destination
        register.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
drivers/net/ethernet/netronome/nfp/bpf/jit.c

index f73242c4da2f2752767fbe8f8480e486a3893845..8a92088df0d7bf4966342631fb412b94f79a3ff4 100644 (file)
@@ -1919,29 +1919,26 @@ static int shr_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 /* Code logic is the same as __shr_imm64 except ashr requires signedness bit
  * told through PREV_ALU result.
  */
-static int ashr_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+static int __ashr_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
 {
-       const struct bpf_insn *insn = &meta->insn;
-       u8 dst = insn->dst_reg * 2;
-
-       if (insn->imm < 32) {
+       if (shift_amt < 32) {
                emit_shf(nfp_prog, reg_both(dst), reg_a(dst + 1), SHF_OP_NONE,
-                        reg_b(dst), SHF_SC_R_DSHF, insn->imm);
+                        reg_b(dst), SHF_SC_R_DSHF, shift_amt);
                /* Set signedness bit. */
                emit_alu(nfp_prog, reg_none(), reg_a(dst + 1), ALU_OP_OR,
                         reg_imm(0));
                emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR,
-                        reg_b(dst + 1), SHF_SC_R_SHF, insn->imm);
-       } else if (insn->imm == 32) {
+                        reg_b(dst + 1), SHF_SC_R_SHF, shift_amt);
+       } else if (shift_amt == 32) {
                /* NOTE: this also helps setting signedness bit. */
                wrp_reg_mov(nfp_prog, dst, dst + 1);
                emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR,
                         reg_b(dst + 1), SHF_SC_R_SHF, 31);
-       } else if (insn->imm > 32) {
+       } else if (shift_amt > 32) {
                emit_alu(nfp_prog, reg_none(), reg_a(dst + 1), ALU_OP_OR,
                         reg_imm(0));
                emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR,
-                        reg_b(dst + 1), SHF_SC_R_SHF, insn->imm - 32);
+                        reg_b(dst + 1), SHF_SC_R_SHF, shift_amt - 32);
                emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR,
                         reg_b(dst + 1), SHF_SC_R_SHF, 31);
        }
@@ -1949,6 +1946,87 @@ static int ashr_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
        return 0;
 }
 
+static int ashr_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+       const struct bpf_insn *insn = &meta->insn;
+       u8 dst = insn->dst_reg * 2;
+
+       return __ashr_imm64(nfp_prog, dst, insn->imm);
+}
+
+static void ashr_reg64_lt32_high(struct nfp_prog *nfp_prog, u8 dst, u8 src)
+{
+       /* NOTE: the first insn will set both indirect shift amount (source A)
+        * and signedness bit (MSB of result).
+        */
+       emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_b(dst + 1));
+       emit_shf_indir(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR,
+                      reg_b(dst + 1), SHF_SC_R_SHF);
+}
+
+static void ashr_reg64_lt32_low(struct nfp_prog *nfp_prog, u8 dst, u8 src)
+{
+       /* NOTE: it is the same as logic shift because we don't need to shift in
+        * signedness bit when the shift amount is less than 32.
+        */
+       return shr_reg64_lt32_low(nfp_prog, dst, src);
+}
+
+static void ashr_reg64_lt32(struct nfp_prog *nfp_prog, u8 dst, u8 src)
+{
+       ashr_reg64_lt32_low(nfp_prog, dst, src);
+       ashr_reg64_lt32_high(nfp_prog, dst, src);
+}
+
+static void ashr_reg64_ge32(struct nfp_prog *nfp_prog, u8 dst, u8 src)
+{
+       emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_b(dst + 1));
+       emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_ASHR,
+                      reg_b(dst + 1), SHF_SC_R_SHF);
+       emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR,
+                reg_b(dst + 1), SHF_SC_R_SHF, 31);
+}
+
+/* Like ashr_imm64, but need to use indirect shift. */
+static int ashr_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+       const struct bpf_insn *insn = &meta->insn;
+       u64 umin, umax;
+       u8 dst, src;
+
+       dst = insn->dst_reg * 2;
+       umin = meta->umin;
+       umax = meta->umax;
+       if (umin == umax)
+               return __ashr_imm64(nfp_prog, dst, umin);
+
+       src = insn->src_reg * 2;
+       if (umax < 32) {
+               ashr_reg64_lt32(nfp_prog, dst, src);
+       } else if (umin >= 32) {
+               ashr_reg64_ge32(nfp_prog, dst, src);
+       } else {
+               u16 label_ge32, label_end;
+
+               label_ge32 = nfp_prog_current_offset(nfp_prog) + 6;
+               emit_br_bset(nfp_prog, reg_a(src), 5, label_ge32, 0);
+               ashr_reg64_lt32_low(nfp_prog, dst, src);
+               label_end = nfp_prog_current_offset(nfp_prog) + 6;
+               emit_br(nfp_prog, BR_UNC, label_end, 2);
+               /* ashr_reg64_lt32_high packed in delay slot. */
+               ashr_reg64_lt32_high(nfp_prog, dst, src);
+
+               if (!nfp_prog_confirm_current_offset(nfp_prog, label_ge32))
+                       return -EINVAL;
+               ashr_reg64_ge32(nfp_prog, dst, src);
+
+               if (!nfp_prog_confirm_current_offset(nfp_prog, label_end))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int mov_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
        const struct bpf_insn *insn = &meta->insn;
@@ -2775,6 +2853,7 @@ static const instr_cb_t instr_cb[256] = {
        [BPF_ALU64 | BPF_LSH | BPF_K] = shl_imm64,
        [BPF_ALU64 | BPF_RSH | BPF_X] = shr_reg64,
        [BPF_ALU64 | BPF_RSH | BPF_K] = shr_imm64,
+       [BPF_ALU64 | BPF_ARSH | BPF_X] = ashr_reg64,
        [BPF_ALU64 | BPF_ARSH | BPF_K] = ashr_imm64,
        [BPF_ALU | BPF_MOV | BPF_X] =   mov_reg,
        [BPF_ALU | BPF_MOV | BPF_K] =   mov_imm,