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 | case BPF_JMP32 | BPF_JSGE | BPF_K: | 792 | case BPF_JMP32 | BPF_JSGE | BPF_K: |
| 793 | case BPF_JMP | BPF_JSLE | BPF_K: | 793 | case BPF_JMP | BPF_JSLE | BPF_K: |
| 794 | case BPF_JMP32 | BPF_JSLE | BPF_K: | 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 | rvoff = rv_offset(i, off, ctx); | 795 | rvoff = rv_offset(i, off, ctx); |
| 798 | s = ctx->ninsns; | 796 | s = ctx->ninsns; |
| 799 | if (imm) { | 797 | if (imm) { |
| @@ -813,15 +811,28 @@ out_be: · 13 unmodified lines | |||
| 813 | 811 | ||
| 814 | /* Adjust for extra insns */ | 812 | /* Adjust for extra insns */ |
| 815 | rvoff -= (e - s) << 2; | 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) { | 817 | case BPF_JMP | BPF_JSET | BPF_K: |
| 818 | /* Adjust for and */ | 818 | case BPF_JMP32 | BPF_JSET | BPF_K: |
| 819 | rvoff -= 4; | 819 | rvoff = rv_offset(i, off, ctx); |
| 820 | emit(rv_and(rs, rd, rs), ctx); | 820 | s = ctx->ninsns; |
| 821 | emit_branch(BPF_JNE, rs, RV_REG_ZERO, rvoff, ctx); | 821 | if (is_12b_int(imm)) { |
| 822 | emit(rv_andi(RV_REG_T1, rd, imm), ctx); | ||
| 822 | } else { | 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 | break; | 836 | break; |
| 826 | 837 | ||
| 827 | /* function call */ | 838 | /* function call */ |