|
@@ -211,6 +211,60 @@ emit_br(struct nfp_prog *nfp_prog, enum br_mask mask, u16 addr, u8 defer)
|
|
emit_br_relo(nfp_prog, mask, addr, defer, RELO_BR_REL);
|
|
emit_br_relo(nfp_prog, mask, addr, defer, RELO_BR_REL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void
|
|
|
|
+__emit_br_bit(struct nfp_prog *nfp_prog, u16 areg, u16 breg, u16 addr, u8 defer,
|
|
|
|
+ bool set, bool src_lmextn)
|
|
|
|
+{
|
|
|
|
+ u16 addr_lo, addr_hi;
|
|
|
|
+ u64 insn;
|
|
|
|
+
|
|
|
|
+ addr_lo = addr & (OP_BR_BIT_ADDR_LO >> __bf_shf(OP_BR_BIT_ADDR_LO));
|
|
|
|
+ addr_hi = addr != addr_lo;
|
|
|
|
+
|
|
|
|
+ insn = OP_BR_BIT_BASE |
|
|
|
|
+ FIELD_PREP(OP_BR_BIT_A_SRC, areg) |
|
|
|
|
+ FIELD_PREP(OP_BR_BIT_B_SRC, breg) |
|
|
|
|
+ FIELD_PREP(OP_BR_BIT_BV, set) |
|
|
|
|
+ FIELD_PREP(OP_BR_BIT_DEFBR, defer) |
|
|
|
|
+ FIELD_PREP(OP_BR_BIT_ADDR_LO, addr_lo) |
|
|
|
|
+ FIELD_PREP(OP_BR_BIT_ADDR_HI, addr_hi) |
|
|
|
|
+ FIELD_PREP(OP_BR_BIT_SRC_LMEXTN, src_lmextn);
|
|
|
|
+
|
|
|
|
+ nfp_prog_push(nfp_prog, insn);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+emit_br_bit_relo(struct nfp_prog *nfp_prog, swreg src, u8 bit, u16 addr,
|
|
|
|
+ u8 defer, bool set, enum nfp_relo_type relo)
|
|
|
|
+{
|
|
|
|
+ struct nfp_insn_re_regs reg;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ /* NOTE: The bit to test is specified as an rotation amount, such that
|
|
|
|
+ * the bit to test will be placed on the MSB of the result when
|
|
|
|
+ * doing a rotate right. For bit X, we need right rotate X + 1.
|
|
|
|
+ */
|
|
|
|
+ bit += 1;
|
|
|
|
+
|
|
|
|
+ err = swreg_to_restricted(reg_none(), src, reg_imm(bit), ®, false);
|
|
|
|
+ if (err) {
|
|
|
|
+ nfp_prog->error = err;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ __emit_br_bit(nfp_prog, reg.areg, reg.breg, addr, defer, set,
|
|
|
|
+ reg.src_lmextn);
|
|
|
|
+
|
|
|
|
+ nfp_prog->prog[nfp_prog->prog_len - 1] |=
|
|
|
|
+ FIELD_PREP(OP_RELO_TYPE, relo);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+emit_br_bset(struct nfp_prog *nfp_prog, swreg src, u8 bit, u16 addr, u8 defer)
|
|
|
|
+{
|
|
|
|
+ emit_br_bit_relo(nfp_prog, src, bit, addr, defer, true, RELO_BR_REL);
|
|
|
|
+}
|
|
|
|
+
|
|
static void
|
|
static void
|
|
__emit_immed(struct nfp_prog *nfp_prog, u16 areg, u16 breg, u16 imm_hi,
|
|
__emit_immed(struct nfp_prog *nfp_prog, u16 areg, u16 breg, u16 imm_hi,
|
|
enum immed_width width, bool invert,
|
|
enum immed_width width, bool invert,
|
|
@@ -309,6 +363,19 @@ emit_shf(struct nfp_prog *nfp_prog, swreg dst,
|
|
reg.dst_lmextn, reg.src_lmextn);
|
|
reg.dst_lmextn, reg.src_lmextn);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void
|
|
|
|
+emit_shf_indir(struct nfp_prog *nfp_prog, swreg dst,
|
|
|
|
+ swreg lreg, enum shf_op op, swreg rreg, enum shf_sc sc)
|
|
|
|
+{
|
|
|
|
+ if (sc == SHF_SC_R_ROT) {
|
|
|
|
+ pr_err("indirect shift is not allowed on rotation\n");
|
|
|
|
+ nfp_prog->error = -EFAULT;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ emit_shf(nfp_prog, dst, lreg, op, rreg, sc, 0);
|
|
|
|
+}
|
|
|
|
+
|
|
static void
|
|
static void
|
|
__emit_alu(struct nfp_prog *nfp_prog, u16 dst, enum alu_dst_ab dst_ab,
|
|
__emit_alu(struct nfp_prog *nfp_prog, u16 dst, enum alu_dst_ab dst_ab,
|
|
u16 areg, enum alu_op op, u16 breg, bool swap, bool wr_both,
|
|
u16 areg, enum alu_op op, u16 breg, bool swap, bool wr_both,
|
|
@@ -1629,26 +1696,142 @@ static int neg_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Pseudo code:
|
|
|
|
+ * if shift_amt >= 32
|
|
|
|
+ * dst_high = dst_low << shift_amt[4:0]
|
|
|
|
+ * dst_low = 0;
|
|
|
|
+ * else
|
|
|
|
+ * dst_high = (dst_high, dst_low) >> (32 - shift_amt)
|
|
|
|
+ * dst_low = dst_low << shift_amt
|
|
|
|
+ *
|
|
|
|
+ * The indirect shift will use the same logic at runtime.
|
|
|
|
+ */
|
|
|
|
+static int __shl_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
|
|
|
|
+{
|
|
|
|
+ if (shift_amt < 32) {
|
|
|
|
+ emit_shf(nfp_prog, reg_both(dst + 1), reg_a(dst + 1),
|
|
|
|
+ SHF_OP_NONE, reg_b(dst), SHF_SC_R_DSHF,
|
|
|
|
+ 32 - shift_amt);
|
|
|
|
+ emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst), SHF_SC_L_SHF, shift_amt);
|
|
|
|
+ } else if (shift_amt == 32) {
|
|
|
|
+ wrp_reg_mov(nfp_prog, dst + 1, dst);
|
|
|
|
+ wrp_immed(nfp_prog, reg_both(dst), 0);
|
|
|
|
+ } else if (shift_amt > 32) {
|
|
|
|
+ emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst), SHF_SC_L_SHF, shift_amt - 32);
|
|
|
|
+ wrp_immed(nfp_prog, reg_both(dst), 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int shl_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
|
|
static int shl_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
|
|
{
|
|
{
|
|
const struct bpf_insn *insn = &meta->insn;
|
|
const struct bpf_insn *insn = &meta->insn;
|
|
u8 dst = insn->dst_reg * 2;
|
|
u8 dst = insn->dst_reg * 2;
|
|
|
|
|
|
- if (insn->imm < 32) {
|
|
|
|
- emit_shf(nfp_prog, reg_both(dst + 1),
|
|
|
|
- reg_a(dst + 1), SHF_OP_NONE, reg_b(dst),
|
|
|
|
- SHF_SC_R_DSHF, 32 - insn->imm);
|
|
|
|
- emit_shf(nfp_prog, reg_both(dst),
|
|
|
|
- reg_none(), SHF_OP_NONE, reg_b(dst),
|
|
|
|
- SHF_SC_L_SHF, insn->imm);
|
|
|
|
- } else if (insn->imm == 32) {
|
|
|
|
- wrp_reg_mov(nfp_prog, dst + 1, dst);
|
|
|
|
- wrp_immed(nfp_prog, reg_both(dst), 0);
|
|
|
|
- } else if (insn->imm > 32) {
|
|
|
|
- emit_shf(nfp_prog, reg_both(dst + 1),
|
|
|
|
- reg_none(), SHF_OP_NONE, reg_b(dst),
|
|
|
|
- SHF_SC_L_SHF, insn->imm - 32);
|
|
|
|
- wrp_immed(nfp_prog, reg_both(dst), 0);
|
|
|
|
|
|
+ return __shl_imm64(nfp_prog, dst, insn->imm);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void shl_reg64_lt32_high(struct nfp_prog *nfp_prog, u8 dst, u8 src)
|
|
|
|
+{
|
|
|
|
+ emit_alu(nfp_prog, imm_both(nfp_prog), reg_imm(32), ALU_OP_SUB,
|
|
|
|
+ reg_b(src));
|
|
|
|
+ emit_alu(nfp_prog, reg_none(), imm_a(nfp_prog), ALU_OP_OR, reg_imm(0));
|
|
|
|
+ emit_shf_indir(nfp_prog, reg_both(dst + 1), reg_a(dst + 1), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst), SHF_SC_R_DSHF);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* NOTE: for indirect left shift, HIGH part should be calculated first. */
|
|
|
|
+static void shl_reg64_lt32_low(struct nfp_prog *nfp_prog, u8 dst, u8 src)
|
|
|
|
+{
|
|
|
|
+ emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0));
|
|
|
|
+ emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst), SHF_SC_L_SHF);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void shl_reg64_lt32(struct nfp_prog *nfp_prog, u8 dst, u8 src)
|
|
|
|
+{
|
|
|
|
+ shl_reg64_lt32_high(nfp_prog, dst, src);
|
|
|
|
+ shl_reg64_lt32_low(nfp_prog, dst, src);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void shl_reg64_ge32(struct nfp_prog *nfp_prog, u8 dst, u8 src)
|
|
|
|
+{
|
|
|
|
+ emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0));
|
|
|
|
+ emit_shf_indir(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst), SHF_SC_L_SHF);
|
|
|
|
+ wrp_immed(nfp_prog, reg_both(dst), 0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int shl_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 __shl_imm64(nfp_prog, dst, umin);
|
|
|
|
+
|
|
|
|
+ src = insn->src_reg * 2;
|
|
|
|
+ if (umax < 32) {
|
|
|
|
+ shl_reg64_lt32(nfp_prog, dst, src);
|
|
|
|
+ } else if (umin >= 32) {
|
|
|
|
+ shl_reg64_ge32(nfp_prog, dst, src);
|
|
|
|
+ } else {
|
|
|
|
+ /* Generate different instruction sequences depending on runtime
|
|
|
|
+ * value of shift amount.
|
|
|
|
+ */
|
|
|
|
+ u16 label_ge32, label_end;
|
|
|
|
+
|
|
|
|
+ label_ge32 = nfp_prog_current_offset(nfp_prog) + 7;
|
|
|
|
+ emit_br_bset(nfp_prog, reg_a(src), 5, label_ge32, 0);
|
|
|
|
+
|
|
|
|
+ shl_reg64_lt32_high(nfp_prog, dst, src);
|
|
|
|
+ label_end = nfp_prog_current_offset(nfp_prog) + 6;
|
|
|
|
+ emit_br(nfp_prog, BR_UNC, label_end, 2);
|
|
|
|
+ /* shl_reg64_lt32_low packed in delay slot. */
|
|
|
|
+ shl_reg64_lt32_low(nfp_prog, dst, src);
|
|
|
|
+
|
|
|
|
+ if (!nfp_prog_confirm_current_offset(nfp_prog, label_ge32))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ shl_reg64_ge32(nfp_prog, dst, src);
|
|
|
|
+
|
|
|
|
+ if (!nfp_prog_confirm_current_offset(nfp_prog, label_end))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Pseudo code:
|
|
|
|
+ * if shift_amt >= 32
|
|
|
|
+ * dst_high = 0;
|
|
|
|
+ * dst_low = dst_high >> shift_amt[4:0]
|
|
|
|
+ * else
|
|
|
|
+ * dst_high = dst_high >> shift_amt
|
|
|
|
+ * dst_low = (dst_high, dst_low) >> shift_amt
|
|
|
|
+ *
|
|
|
|
+ * The indirect shift will use the same logic at runtime.
|
|
|
|
+ */
|
|
|
|
+static int __shr_imm64(struct nfp_prog *nfp_prog, u8 dst, u8 shift_amt)
|
|
|
|
+{
|
|
|
|
+ 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, shift_amt);
|
|
|
|
+ emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst + 1), SHF_SC_R_SHF, shift_amt);
|
|
|
|
+ } else if (shift_amt == 32) {
|
|
|
|
+ wrp_reg_mov(nfp_prog, dst, dst + 1);
|
|
|
|
+ wrp_immed(nfp_prog, reg_both(dst + 1), 0);
|
|
|
|
+ } else if (shift_amt > 32) {
|
|
|
|
+ emit_shf(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst + 1), SHF_SC_R_SHF, shift_amt - 32);
|
|
|
|
+ wrp_immed(nfp_prog, reg_both(dst + 1), 0);
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -1659,21 +1842,186 @@ static int shr_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
|
|
const struct bpf_insn *insn = &meta->insn;
|
|
const struct bpf_insn *insn = &meta->insn;
|
|
u8 dst = insn->dst_reg * 2;
|
|
u8 dst = insn->dst_reg * 2;
|
|
|
|
|
|
- if (insn->imm < 32) {
|
|
|
|
- emit_shf(nfp_prog, reg_both(dst),
|
|
|
|
- reg_a(dst + 1), SHF_OP_NONE, reg_b(dst),
|
|
|
|
- SHF_SC_R_DSHF, insn->imm);
|
|
|
|
- emit_shf(nfp_prog, reg_both(dst + 1),
|
|
|
|
- reg_none(), SHF_OP_NONE, reg_b(dst + 1),
|
|
|
|
- SHF_SC_R_SHF, insn->imm);
|
|
|
|
- } else if (insn->imm == 32) {
|
|
|
|
|
|
+ return __shr_imm64(nfp_prog, dst, insn->imm);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* NOTE: for indirect right shift, LOW part should be calculated first. */
|
|
|
|
+static void shr_reg64_lt32_high(struct nfp_prog *nfp_prog, u8 dst, u8 src)
|
|
|
|
+{
|
|
|
|
+ emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0));
|
|
|
|
+ emit_shf_indir(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst + 1), SHF_SC_R_SHF);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void shr_reg64_lt32_low(struct nfp_prog *nfp_prog, u8 dst, u8 src)
|
|
|
|
+{
|
|
|
|
+ emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0));
|
|
|
|
+ emit_shf_indir(nfp_prog, reg_both(dst), reg_a(dst + 1), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst), SHF_SC_R_DSHF);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void shr_reg64_lt32(struct nfp_prog *nfp_prog, u8 dst, u8 src)
|
|
|
|
+{
|
|
|
|
+ shr_reg64_lt32_low(nfp_prog, dst, src);
|
|
|
|
+ shr_reg64_lt32_high(nfp_prog, dst, src);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void shr_reg64_ge32(struct nfp_prog *nfp_prog, u8 dst, u8 src)
|
|
|
|
+{
|
|
|
|
+ emit_alu(nfp_prog, reg_none(), reg_a(src), ALU_OP_OR, reg_imm(0));
|
|
|
|
+ emit_shf_indir(nfp_prog, reg_both(dst), reg_none(), SHF_OP_NONE,
|
|
|
|
+ reg_b(dst + 1), SHF_SC_R_SHF);
|
|
|
|
+ wrp_immed(nfp_prog, reg_both(dst + 1), 0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int shr_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 __shr_imm64(nfp_prog, dst, umin);
|
|
|
|
+
|
|
|
|
+ src = insn->src_reg * 2;
|
|
|
|
+ if (umax < 32) {
|
|
|
|
+ shr_reg64_lt32(nfp_prog, dst, src);
|
|
|
|
+ } else if (umin >= 32) {
|
|
|
|
+ shr_reg64_ge32(nfp_prog, dst, src);
|
|
|
|
+ } else {
|
|
|
|
+ /* Generate different instruction sequences depending on runtime
|
|
|
|
+ * value of shift amount.
|
|
|
|
+ */
|
|
|
|
+ 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);
|
|
|
|
+ shr_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);
|
|
|
|
+ /* shr_reg64_lt32_high packed in delay slot. */
|
|
|
|
+ shr_reg64_lt32_high(nfp_prog, dst, src);
|
|
|
|
+
|
|
|
|
+ if (!nfp_prog_confirm_current_offset(nfp_prog, label_ge32))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ shr_reg64_ge32(nfp_prog, dst, src);
|
|
|
|
+
|
|
|
|
+ if (!nfp_prog_confirm_current_offset(nfp_prog, label_end))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 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, u8 dst, u8 shift_amt)
|
|
|
|
+{
|
|
|
|
+ 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, 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, shift_amt);
|
|
|
|
+ } else if (shift_amt == 32) {
|
|
|
|
+ /* NOTE: this also helps setting signedness bit. */
|
|
wrp_reg_mov(nfp_prog, dst, dst + 1);
|
|
wrp_reg_mov(nfp_prog, dst, dst + 1);
|
|
- wrp_immed(nfp_prog, reg_both(dst + 1), 0);
|
|
|
|
- } else if (insn->imm > 32) {
|
|
|
|
- emit_shf(nfp_prog, reg_both(dst),
|
|
|
|
- reg_none(), SHF_OP_NONE, reg_b(dst + 1),
|
|
|
|
- SHF_SC_R_SHF, insn->imm - 32);
|
|
|
|
- wrp_immed(nfp_prog, reg_both(dst + 1), 0);
|
|
|
|
|
|
+ emit_shf(nfp_prog, reg_both(dst + 1), reg_none(), SHF_OP_ASHR,
|
|
|
|
+ reg_b(dst + 1), SHF_SC_R_SHF, 31);
|
|
|
|
+ } 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, 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);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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;
|
|
return 0;
|
|
@@ -2501,8 +2849,12 @@ static const instr_cb_t instr_cb[256] = {
|
|
[BPF_ALU64 | BPF_SUB | BPF_X] = sub_reg64,
|
|
[BPF_ALU64 | BPF_SUB | BPF_X] = sub_reg64,
|
|
[BPF_ALU64 | BPF_SUB | BPF_K] = sub_imm64,
|
|
[BPF_ALU64 | BPF_SUB | BPF_K] = sub_imm64,
|
|
[BPF_ALU64 | BPF_NEG] = neg_reg64,
|
|
[BPF_ALU64 | BPF_NEG] = neg_reg64,
|
|
|
|
+ [BPF_ALU64 | BPF_LSH | BPF_X] = shl_reg64,
|
|
[BPF_ALU64 | BPF_LSH | BPF_K] = shl_imm64,
|
|
[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_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_X] = mov_reg,
|
|
[BPF_ALU | BPF_MOV | BPF_K] = mov_imm,
|
|
[BPF_ALU | BPF_MOV | BPF_K] = mov_imm,
|
|
[BPF_ALU | BPF_XOR | BPF_X] = xor_reg,
|
|
[BPF_ALU | BPF_XOR | BPF_X] = xor_reg,
|