This patch optimizes BPF_JSET BPF_K by using a RISC-V andi instruction when the BPF immediate fits in 12 bits, instead of first loading the immediate to a temporary register. Examples of generated code with and without this optimization: BPF_JMP_IMM(BPF_JSET, R1, 2, 1) without optimization: 20: li t1,2 24: and t1,a0,t1 28: bnez t1,0x30 BPF_JMP_IMM(BPF_JSET, R1, 2, 1) with optimization: 20: andi t1,a0,2 24: bnez t1,0x2c BPF_JMP32_IMM(BPF_JSET, R1, 2, 1) without optimization: 20: li t1,2 24: mv t2,a0 28: slli t2,t2,0x20 2c: srli t2,t2,0x20 30: slli t1,t1,0x20 34: srli t1,t1,0x20 38: and t1,t2,t1 3c: bnez t1,0x44 BPF_JMP32_IMM(BPF_JSET, R1, 2, 1) with optimization: 20: andi t1,a0,2 24: bnez t1,0x2c In these examples, because the upper 32 bits of the sign-extended immediate are 0, BPF_JMP BPF_JSET and BPF_JMP32 BPF_JSET are equivalent and therefore the JIT produces identical code for them.
Unified · Split
| @@ -792,8 +792,6 @@ out_be: · 791 unmodified lines | ||
| 792 | 792 | case BPF_JMP32 | BPF_JSGE | BPF_K: |
| 793 | 793 | case BPF_JMP | BPF_JSLE | BPF_K: |
| 794 | 794 | case BPF_JMP32 | BPF_JSLE | BPF_K: |
| 795 | − case BPF_JMP | BPF_JSET | BPF_K: | |
| 796 | − case BPF_JMP32 | BPF_JSET | BPF_K: | |
| 797 | 795 | rvoff = rv_offset(i, off, ctx); |
| 798 | 796 | s = ctx->ninsns; |
| 799 | 797 | if (imm) { |
| @@ -813,15 +811,28 @@ out_be: · 13 unmodified lines | ||
| 813 | 811 | |
| 814 | 812 | /* Adjust for extra insns */ |
| 815 | 813 | rvoff -= (e - s) << 2; |
| 814 | + emit_branch(BPF_OP(code), rd, rs, rvoff, ctx); | |
| 815 | + break; | |
| 816 | 816 | |
| 817 | − if (BPF_OP(code) == BPF_JSET) { | |
| 818 | − /* Adjust for and */ | |
| 819 | − rvoff -= 4; | |
| 820 | − emit(rv_and(rs, rd, rs), ctx); | |
| 821 | − emit_branch(BPF_JNE, rs, RV_REG_ZERO, rvoff, ctx); | |
| 817 | + case BPF_JMP | BPF_JSET | BPF_K: | |
| 818 | + case BPF_JMP32 | BPF_JSET | BPF_K: | |
| 819 | + rvoff = rv_offset(i, off, ctx); | |
| 820 | + s = ctx->ninsns; | |
| 821 | + if (is_12b_int(imm)) { | |
| 822 | + emit(rv_andi(RV_REG_T1, rd, imm), ctx); | |
| 822 | 823 | } else { |
| 823 | − emit_branch(BPF_OP(code), rd, rs, rvoff, ctx); | |
| 824 | + emit_imm(RV_REG_T1, imm, ctx); | |
| 825 | + emit(rv_and(RV_REG_T1, rd, RV_REG_T1), ctx); | |
| 824 | 826 | } |
| 827 | + /* For jset32, we should clear the upper 32 bits of t1, but | |
| 828 | + * sign-extension is sufficient here and saves one instruction, | |
| 829 | + * as t1 is used only in comparison against zero. | |
| 830 | + */ | |
| 831 | + if (!is64 && imm < 0) | |
| 832 | + emit(rv_addiw(RV_REG_T1, RV_REG_T1, 0), ctx); | |
| 833 | + e = ctx->ninsns; | |
| 834 | + rvoff -= (e - s) << 2; | |
| 835 | + emit_branch(BPF_JNE, RV_REG_T1, RV_REG_ZERO, rvoff, ctx); | |
| 825 | 836 | break; |
| 826 | 837 | |
| 827 | 838 | /* function call */ |