/* Instruction lengths in bytes. */ enum {
INSN_len_normal = 4, /* Normal instructions length. */
INSN_len_imm = 4/* Length of an extra 32-bit immediate. */
};
/* ZZ defines the size of operation in encodings that it is used. */ enum {
ZZ_1_byte = 1,
ZZ_2_byte = 2,
ZZ_4_byte = 0,
ZZ_8_byte = 3
};
/* *AAismostlyaboutaddresswritebackmode.Itdeterminesifthe *addressinquestionshouldbeupdatedbeforeusageorafter: *addr+=offset;data=*addr; *data=*addr;addr+=offset; * *In"scaling"mode,theeffectiveaddresswillbecomethesum *of"address"+"index"*"size".The"size"isspecifiedbythe *"ZZ"field.ThereisnowritebackwhenAAissetforscaling: *data=*(addr+offset<<zz)
*/ enum {
AA_none = 0,
AA_pre = 1, /* in assembly known as "a/aw". */
AA_post = 2, /* in assembly known as "ab". */
AA_scale = 3/* in assembly known as "as". */
};
/* X flag determines the mode of extension. */ enum {
X_zero = 0,
X_sign = 1
};
/* Condition codes. */ enum {
CC_always = 0, /* condition is true all the time */
CC_equal = 1, /* if status32.z flag is set */
CC_unequal = 2, /* if status32.z flag is clear */
CC_positive = 3, /* if status32.n flag is clear */
CC_negative = 4, /* if status32.n flag is set */
CC_less_u = 5, /* less than (unsigned) */
CC_less_eq_u = 14, /* less than or equal (unsigned) */
CC_great_eq_u = 6, /* greater than or equal (unsigned) */
CC_great_u = 13, /* greater than (unsigned) */
CC_less_s = 11, /* less than (signed) */
CC_less_eq_s = 12, /* less than or equal (signed) */
CC_great_eq_s = 10, /* greater than or equal (signed) */
CC_great_s = 9/* greater than (signed) */
};
if (buf)
emit_4_bytes(buf, insn); return INSN_len_normal;
}
/* The emitted code may have different sizes based on "imm". */ static u8 arc_mov_i(u8 *buf, u8 rd, s32 imm)
{ const u32 insn = OPC_MOV | OP_B(rd) | OP_IMM;
if (IN_S12_RANGE(imm)) return arc_movi_r(buf, rd, imm);
if (sign_ext) { /* First handle the low 32-bit part. */
len = mov_r32(buf, rd, rs, sign_ext);
/* Now propagate the sign bit of LO to HI. */ if (sign_ext == 8 || sign_ext == 16 || sign_ext == 32) {
len += arc_asri_r(BUF(buf, len),
REG_HI(rd), REG_LO(rd), 31);
}
return len;
}
/* Unsigned move. */
if (rd == rs) return0;
len = arc_mov_r(buf, REG_LO(rd), REG_LO(rs));
if (rs != BPF_REG_FP)
len += arc_mov_r(BUF(buf, len), REG_HI(rd), REG_HI(rs)); /* BPF_REG_FP is mapped to 32-bit "fp" register. */ else
len += arc_movi_r(BUF(buf, len), REG_HI(rd), 0);
return len;
}
/* Sign extend the 32-bit immediate into 64-bit register pair. */
u8 mov_r64_i32(u8 *buf, u8 reg, s32 imm)
{
u8 len = 0;
len = arc_mov_i(buf, REG_LO(reg), imm);
/* BPF_REG_FP is mapped to 32-bit "fp" register. */ if (reg != BPF_REG_FP) { if (imm >= 0)
len += arc_movi_r(BUF(buf, len), REG_HI(reg), 0); else
len += arc_movi_r(BUF(buf, len), REG_HI(reg), -1);
}
/* Use LD.X only if the data size is less than 32-bit. */ if (sign_ext && (zz == ZZ_1_byte || zz == ZZ_2_byte)) {
len += arc_ldx_r(BUF(buf, len), REG_LO(rd),
arc_reg_mem, off, zz);
} else {
len += arc_ld_r(BUF(buf, len), REG_LO(rd),
arc_reg_mem, off, zz);
}
if (sign_ext) { /* Propagate the sign bit to the higher reg. */
len += arc_asri_r(BUF(buf, len),
REG_HI(rd), REG_LO(rd), 31);
} else {
len += arc_movi_r(BUF(buf, len), REG_HI(rd), 0);
}
} elseif (size == BPF_DW) { /* *Weareabouttoissue2consecutiveloads: * *ldrx,[rb,off+0] *ldry,[rb,off+4] * *If"rx"and"rb"arethesameregisters,thentheorder *shouldchangetoguaranteethat"rb"remainsintact *duringthese2operations: * *ldry,[rb,off+4] *ldrx,[rb,off+0]
*/ if (REG_LO(rd) != arc_reg_mem) {
len += arc_ld_r(BUF(buf, len), REG_LO(rd), arc_reg_mem,
off, ZZ_4_byte);
len += arc_ld_r(BUF(buf, len), REG_HI(rd), arc_reg_mem,
off + 4, ZZ_4_byte);
} else {
len += arc_ld_r(BUF(buf, len), REG_HI(rd), arc_reg_mem,
off + 4, ZZ_4_byte);
len += arc_ld_r(BUF(buf, len), REG_LO(rd), arc_reg_mem,
off, ZZ_4_byte);
}
}
/*
* algorithm
* ---------
* if (n <= 32)
* to_hi = lo >> (32-n) # (32-n) is the negate of "n" in a 5-bit width.
* lo <<= n
* hi <<= n
* hi |= to_hi
* else
* hi = lo << (n-32)
* lo = 0
*
* assembly translation for "LSH B, C"
* (heavily influenced by ARC gcc)
* -----------------------------------
* not t0, C_lo # The first 3 lines are almost the same as:
* lsr t1, B_lo, 1 # neg t0, C_lo
* lsr t1, t1, t0 # lsr t1, B_lo, t0 --> t1 is "to_hi"
* mov t0, C_lo* # with one important difference. In "neg"
* asl B_lo, B_lo, t0 # version, when C_lo=0, t1 becomes B_lo while
* asl B_hi, B_hi, t0 # it should be 0. The "not" approach instead,
* or B_hi, B_hi, t1 # "shift"s t1 once and 31 times, practically
* btst t0, 5 # setting it to 0 when C_lo=0.
* mov.ne B_hi, B_lo**
* mov.ne B_lo, 0
*
* *The "mov t0, C_lo" is necessary to cover the cases that C is the same
* register as B.
*
* **ARC performs a shift in this manner: B <<= (C & 31)
* For 32<=n<64, "n-32" and "n&31" are the same. Therefore, "B << n" and
* "B << (n-32)" yield the same results. e.g. the results of "B << 35" and
* "B << 3" are the same.
*
* The behaviour is undefined for n >= 64.
*/
u8 lsh_r64(u8 *buf, u8 rd, u8 rs)
{
const u8 t0 = REG_LO(JIT_REG_TMP);
const u8 t1 = REG_HI(JIT_REG_TMP);
const u8 C_lo = REG_LO(rs);
const u8 B_lo = REG_LO(rd);
const u8 B_hi = REG_HI(rd);
u8 len;
len = arc_not_r(buf, t0, C_lo);
len += arc_lsri_r(BUF(buf, len), t1, B_lo, 1);
len += arc_lsr_r(BUF(buf, len), t1, t1, t0);
len += arc_mov_r(BUF(buf, len), t0, C_lo);
len += arc_asl_r(BUF(buf, len), B_lo, B_lo, t0);
len += arc_asl_r(BUF(buf, len), B_hi, B_hi, t0);
len += arc_or_r(BUF(buf, len), B_hi, B_hi, t1);
len += arc_btst_i(BUF(buf, len), t0, 5);
len += arc_mov_cc_r(BUF(buf, len), CC_unequal, B_hi, B_lo);
len += arc_movu_cc_r(BUF(buf, len), CC_unequal, B_lo, 0);
return len;
}
/*
* if (n < 32)
* to_hi = B_lo >> 32-n # extract upper n bits
* lo <<= n
* hi <<=n
* hi |= to_hi
* else if (n < 64)
* hi = lo << n-32
* lo = 0
*/
u8 lsh_r64_i32(u8 *buf, u8 rd, s32 imm)
{
const u8 t0 = REG_LO(JIT_REG_TMP);
const u8 B_lo = REG_LO(rd);
const u8 B_hi = REG_HI(rd);
const u8 n = (u8)imm;
u8 len = 0;
if (n == 0) {
return 0;
} else if (n <= 31) {
len = arc_lsri_r(buf, t0, B_lo, 32 - n);
len += arc_asli_r(BUF(buf, len), B_lo, B_lo, n);
len += arc_asli_r(BUF(buf, len), B_hi, B_hi, n);
len += arc_or_r(BUF(buf, len), B_hi, B_hi, t0);
} else if (n <= 63) {
len = arc_asli_r(buf, B_hi, B_lo, n - 32);
len += arc_movi_r(BUF(buf, len), B_lo, 0);
}
/* n >= 64 is undefined behaviour. */
/*
* For comparison, see rsh_r64().
*
* algorithm
* ---------
* if (n <= 32)
* to_lo = hi << (32-n)
* hi s>>= n
* lo >>= n
* lo |= to_lo
* else
* hi_sign = hi s>>31
* lo = hi s>> (n-32)
* hi = hi_sign
*
* ARSH B,C
* ----------
* not t0, C_lo
* asl t1, B_hi, 1
* asl t1, t1, t0
* mov t0, C_lo
* asr B_hi, B_hi, t0
* lsr B_lo, B_lo, t0
* or B_lo, B_lo, t1
* btst t0, 5
* asr t0, B_hi, 31 # now, t0 = 0 or -1 based on B_hi's sign
* mov.ne B_lo, B_hi
* mov.ne B_hi, t0
*/
u8 arsh_r64(u8 *buf, u8 rd, u8 rs)
{
const u8 t0 = REG_LO(JIT_REG_TMP);
const u8 t1 = REG_HI(JIT_REG_TMP);
const u8 C_lo = REG_LO(rs);
const u8 B_lo = REG_LO(rd);
const u8 B_hi = REG_HI(rd);
u8 len;
len = arc_not_r(buf, t0, C_lo);
len += arc_asli_r(BUF(buf, len), t1, B_hi, 1);
len += arc_asl_r(BUF(buf, len), t1, t1, t0);
len += arc_mov_r(BUF(buf, len), t0, C_lo);
len += arc_asr_r(BUF(buf, len), B_hi, B_hi, t0);
len += arc_lsr_r(BUF(buf, len), B_lo, B_lo, t0);
len += arc_or_r(BUF(buf, len), B_lo, B_lo, t1);
len += arc_btst_i(BUF(buf, len), t0, 5);
len += arc_asri_r(BUF(buf, len), t0, B_hi, 31);
len += arc_mov_cc_r(BUF(buf, len), CC_unequal, B_lo, B_hi);
len += arc_mov_cc_r(BUF(buf, len), CC_unequal, B_hi, t0);
return len;
}
/*
* if (n < 32)
* to_lo = lo << 32-n # extract lower n bits, right-padded with 32-n 0s
* lo >>=n
* hi s>>=n
* hi |= to_lo
* else if (n < 64)
* lo = hi s>> n-32
* hi = (lo[msb] ? -1 : 0)
*/
u8 arsh_r64_i32(u8 *buf, u8 rd, s32 imm)
{
const u8 t0 = REG_LO(JIT_REG_TMP);
const u8 B_lo = REG_LO(rd);
const u8 B_hi = REG_HI(rd);
const u8 n = (u8)imm;
u8 len = 0;
if (n == 0) {
return 0;
} else if (n <= 31) {
len = arc_asli_r(buf, t0, B_hi, 32 - n);
len += arc_lsri_r(BUF(buf, len), B_lo, B_lo, n);
len += arc_asri_r(BUF(buf, len), B_hi, B_hi, n);
len += arc_or_r(BUF(buf, len), B_lo, B_lo, t0);
} else if (n <= 63) {
len = arc_asri_r(buf, B_lo, B_hi, n - 32);
len += arc_movi_r(BUF(buf, len), B_hi, -1);
len += arc_btst_i(BUF(buf, len), B_lo, 31);
len += arc_movu_cc_r(BUF(buf, len), CC_equal, B_hi, 0);
}
/* n >= 64 is undefined behaviour. */
return len;
}
u8 gen_swap(u8 *buf, u8 rd, u8 size, u8 endian, bool force, bool do_zext)
{
u8 len = 0;
#ifdef __BIG_ENDIAN
const u8 host_endian = BPF_FROM_BE;
#else
const u8 host_endian = BPF_FROM_LE;
#endif
if (host_endian != endian || force) {
switch (size) {
case 16:
/*
* r = B4B3_B2B1 << 16 --> r = B2B1_0000
* then, swape(r) would become the desired 0000_B1B2
*/
len = arc_asli_r(buf, REG_LO(rd), REG_LO(rd), 16);
fallthrough;
case 32:
len += arc_swape_r(BUF(buf, len), REG_LO(rd));
if (do_zext)
len += zext(BUF(buf, len), rd);
break;
case 64:
/*
* swap "hi" and "lo":
* hi ^= lo;
* lo ^= hi;
* hi ^= lo;
* and then swap the bytes in "hi" and "lo".
*/
len = arc_xor_r(buf, REG_HI(rd), REG_LO(rd));
len += arc_xor_r(BUF(buf, len), REG_LO(rd), REG_HI(rd));
len += arc_xor_r(BUF(buf, len), REG_HI(rd), REG_LO(rd));
len += arc_swape_r(BUF(buf, len), REG_LO(rd));
len += arc_swape_r(BUF(buf, len), REG_HI(rd));
break;
default:
/* The caller must have handled this. */
break;
}
} else {
/*
* If the same endianness, there's not much to do other
* than zeroing out the upper bytes based on the "size".
*/
switch (size) {
case 16:
len = arc_and_i(buf, REG_LO(rd), 0xffff);
fallthrough;
case 32:
if (do_zext)
len += zext(BUF(buf, len), rd);
break;
case 64:
break;
default:
/* The caller must have handled this. */
break;
}
}
return len;
}
/*
* To create a frame, all that is needed is:
*
* push fp
* mov fp, sp
* sub sp, <frame_size>
*
* "push fp" is taken care of separately while saving the clobbered registers.
* All that remains is copying SP value to FP and shrinking SP's address space
* for any possible function call to come.
*/
static inline u8 frame_create(u8 *buf, u16 size)
{
u8 len;
len = arc_mov_r(buf, ARC_R_FP, ARC_R_SP);
if (IN_U6_RANGE(size))
len += arc_subi_r(BUF(buf, len), ARC_R_SP, size);
else
len += arc_sub_i(BUF(buf, len), ARC_R_SP, size);
return len;
}
/*
* mov sp, fp
*
* The value of SP upon entering was copied to FP.
*/
static inline u8 frame_restore(u8 *buf)
{
return arc_mov_r(buf, ARC_R_SP, ARC_R_FP);
}
/*
* Going from a JITed code to the native caller:
*
* mov ARC_ABI_RET_lo, BPF_REG_0_lo # r0 <- r8
* mov ARC_ABI_RET_hi, BPF_REG_0_hi # r1 <- r9
*/
static u8 bpf_to_arc_return(u8 *buf)
{
u8 len;
len = arc_mov_r(buf, ARC_R_0, REG_LO(BPF_REG_0));
len += arc_mov_r(BUF(buf, len), ARC_R_1, REG_HI(BPF_REG_0));
return len;
}
/*
* Coming back from an external (in-kernel) function to the JITed code:
*
* mov ARC_ABI_RET_lo, BPF_REG_0_lo # r8 <- r0
* mov ARC_ABI_RET_hi, BPF_REG_0_hi # r9 <- r1
*/
u8 arc_to_bpf_return(u8 *buf)
{
u8 len;
len = arc_mov_r(buf, REG_LO(BPF_REG_0), ARC_R_0);
len += arc_mov_r(BUF(buf, len), REG_HI(BPF_REG_0), ARC_R_1);
return len;
}
/*
* This translation leads to:
*
* mov r10, addr # always an 8-byte instruction
* jl [r10]
*
* The length of the "mov" must be fixed (8), otherwise it may diverge
* during the normal and extra passes:
*
* normal pass extra pass
*
* 180: mov r10,0 | 180: mov r10,0x700578d8
* 184: jl [r10] | 188: jl [r10]
* 188: add.f r16,r16,0x1 | 18c: adc r17,r17,0
* 18c: adc r17,r17,0 |
*
* In the above example, the change from "r10 <- 0" to "r10 <- 0x700578d8"
* has led to an increase in the length of the "mov" instruction.
* Inadvertently, that caused the loss of the "add.f" instruction.
*/
static u8 jump_and_link(u8 *buf, u32 addr)
{
u8 len;
len = arc_mov_i_fixed(buf, REG_LO(JIT_REG_TMP), addr);
len += arc_jl(BUF(buf, len), REG_LO(JIT_REG_TMP));
return len;
}
/*
* This function determines which ARC registers must be saved and restored.
* It does so by looking into:
*
* "bpf_reg": The clobbered (destination) BPF register
* "is_call": Indicator if the current instruction is a call
*
* When a register of interest is clobbered, its corresponding bit position
* in return value, "usage", is set to true.
*/
u32 mask_for_used_regs(u8 bpf_reg, bool is_call)
{
u32 usage = 0;
/* BPF registers that must be saved. */
if (bpf_reg >= BPF_REG_6 && bpf_reg <= BPF_REG_9) {
usage |= BIT(REG_LO(bpf_reg));
usage |= BIT(REG_HI(bpf_reg));
/*
* Using the frame pointer register implies that it should
* be saved and reinitialised with the current frame data.
*/
} else if (bpf_reg == BPF_REG_FP) {
usage |= BIT(REG_LO(BPF_REG_FP));
/* Could there be some ARC registers that must to be saved? */
} else {
if (REG_LO(bpf_reg) >= ARC_CALLEE_SAVED_REG_FIRST &&
REG_LO(bpf_reg) <= ARC_CALLEE_SAVED_REG_LAST)
usage |= BIT(REG_LO(bpf_reg));
/* A "call" indicates that ARC's "blink" reg must be saved. */
usage |= is_call ? BIT(ARC_R_BLINK) : 0;
return usage;
}
/*
* push blink # if blink is marked as clobbered
* push r[0-n] # if r[i] is marked as clobbered
* push fp # if fp is marked as clobbered
* mov fp, sp # if frame_size > 0 (clobbers fp)
* sub sp, <frame_size> # same as above
*/
u8 arc_prologue(u8 *buf, u32 usage, u16 frame_size)
{
u8 len = 0;
u32 gp_regs = 0;
/* Deal with blink first. */
if (usage & BIT(ARC_R_BLINK))
len += arc_push_r(BUF(buf, len), ARC_R_BLINK);
len += arc_push_r(BUF(buf, len), reg);
gp_regs &= ~BIT(reg);
}
/* Deal with fp last. */
if ((usage & BIT(ARC_R_FP)) || frame_size > 0)
len += arc_push_r(BUF(buf, len), ARC_R_FP);
if (frame_size > 0)
len += frame_create(BUF(buf, len), frame_size);
#ifdef ARC_BPF_JIT_DEBUG
if ((usage & BIT(ARC_R_FP)) && frame_size == 0) {
pr_err("FP is being saved while there is no frame.");
BUG();
}
#endif
return len;
}
/*
* mov sp, fp # if frame_size > 0
* pop fp # if fp is marked as clobbered
* pop r[n-0] # if r[i] is marked as clobbered
* pop blink # if blink is marked as clobbered
* mov r0, r8 # always: ABI_return <- BPF_return
* mov r1, r9 # continuation of above
* j [blink] # always
*
* "fp being marked as clobbered" and "frame_size > 0" are the two sides of
* the same coin.
*/
u8 arc_epilogue(u8 *buf, u32 usage, u16 frame_size)
{
u32 len = 0;
u32 gp_regs = 0;
#ifdef ARC_BPF_JIT_DEBUG
if ((usage & BIT(ARC_R_FP)) && frame_size == 0) {
pr_err("FP is being saved while there is no frame.");
BUG();
}
#endif
if (frame_size > 0)
len += frame_restore(BUF(buf, len));
/* Deal with fp first. */
if ((usage & BIT(ARC_R_FP)) || frame_size > 0)
len += arc_pop_r(BUF(buf, len), ARC_R_FP);
gp_regs = usage & ~(BIT(ARC_R_BLINK) | BIT(ARC_R_FP));
while (gp_regs) {
/* "usage" is 32-bit, each bit indicating an ARC register. */
u8 reg = 31 - __builtin_clz(gp_regs);
len += arc_pop_r(BUF(buf, len), reg);
gp_regs &= ~BIT(reg);
}
/* Deal with blink last. */
if (usage & BIT(ARC_R_BLINK))
len += arc_pop_r(BUF(buf, len), ARC_R_BLINK);
/* Wrap up the return value and jump back to the caller. */
len += bpf_to_arc_return(BUF(buf, len));
len += arc_jmp_return(BUF(buf, len));
return len;
}
/*
* For details on the algorithm, see the comments of "gen_jcc_64()".
*
* This data structure is holding information for jump translations.
*
* jit_off: How many bytes into the current JIT address, "b"ranch insn. occurs
* cond: The condition that the ARC branch instruction must use
*
* e.g.:
*
* BPF_JGE R1, R0, @target
* ------------------------
* |
* v
* 0x1000: cmp r3, r1 # 0x1000 is the JIT address for "BPF_JGE ..." insn
* 0x1004: bhi @target # first jump (branch higher)
* 0x1008: blo @end # second jump acting as a skip (end is 0x1014)
* 0x100C: cmp r2, r0 # the lower 32 bits are evaluated
* 0x1010: bhs @target # third jump (branch higher or same)
* 0x1014: ...
*
* The jit_off(set) of the "bhi" is 4 bytes.
* The cond(ition) for the "bhi" is "CC_great_u".
*
* The jit_off(set) is necessary for calculating the exact displacement
* to the "target" address:
*
* jit_address + jit_off(set) - @target
* 0x1000 + 4 - @target
*/
#define JCC64_NR_OF_JMPS 3 /* Number of jumps in jcc64 template. */
#define JCC64_INSNS_TO_END 3 /* Number of insn. inclusive the 2nd jmp to end. */
#define JCC64_SKIP_JMP 1 /* Index of the "skip" jump to "end". */
static const struct {
/*
* "jit_off" is common between all "jmp[]" and is coupled with
* "cond" of each "jmp[]" instance. e.g.:
*
* arcv2_64_jccs.jit_off[1]
* arcv2_64_jccs.jmp[ARC_CC_UGT].cond[1]
*
* Are indicating that the second jump in JITed code of "UGT"
* is at offset "jit_off[1]" while its condition is "cond[1]".
*/
u8 jit_off[JCC64_NR_OF_JMPS];
/*
* The displacement (offset) for ARC's "b"ranch instruction is the distance
* from the aligned version of _current_ instruction (PCL) to the target
* instruction:
*
* DISP = TARGET - PCL # PCL is the word aligned PC
*/
static inline s32 get_displacement(u32 curr_off, u32 targ_off)
{
return (s32)(targ_off - (curr_off & ~3L));
}
/*
* "disp"lacement should be:
*
* 1. 16-bit aligned.
* 2. fit in S25, because no "condition code" is supposed to be encoded.
*/
static inline bool is_valid_far_disp(s32 disp)
{
return (!(disp & 1) && IN_S25_RANGE(disp));
}
/*
* "disp"lacement should be:
*
* 1. 16-bit aligned.
* 2. fit in S21, because "condition code" is supposed to be encoded too.
*/
static inline bool is_valid_near_disp(s32 disp)
{
return (!(disp & 1) && IN_S21_RANGE(disp));
}
len += arc_tst_r(BUF(buf, len), REG_HI(rd), REG_HI(rs));
len += arc_tstz_r(BUF(buf, len), REG_LO(rd), REG_LO(rs));
disp = get_displacement(curr_off + len, targ_off);
len += arc_bcc(BUF(buf, len), CC_unequal, disp);
return len;
}
/*
* Verify if all the jumps for a JITed jcc64 operation are valid,
* by consulting the data stored at "arcv2_64_jccs".
*/
static bool check_jcc_64(u32 curr_off, u32 targ_off, u8 cond)
{
size_t i;
if (cond >= ARC_CC_LAST)
return false;
for (i = 0; i < JCC64_NR_OF_JMPS; i++) {
u32 from, to;
from = curr_off + arcv2_64_jccs.jit_off[i];
/* for the 2nd jump, we jump to the end of block. */
if (i != JCC64_SKIP_JMP)
to = targ_off;
else
to = from + (JCC64_INSNS_TO_END * INSN_len_normal);
/* There is a "cc" in the instruction, so a "near" jump. */
if (!is_valid_near_disp(get_displacement(from, to)))
return false;
}
return true;
}
/* Can the jump from "curr_off" to "targ_off" actually happen? */
bool check_jmp_64(u32 curr_off, u32 targ_off, u8 cond)
{
s32 disp;
switch (cond) {
case ARC_CC_UGT:
case ARC_CC_UGE:
case ARC_CC_ULT:
case ARC_CC_ULE:
case ARC_CC_SGT:
case ARC_CC_SGE:
case ARC_CC_SLT:
case ARC_CC_SLE:
return check_jcc_64(curr_off, targ_off, cond);
case ARC_CC_EQ:
case ARC_CC_NE:
case ARC_CC_SET:
/*
* The "jump" for the JITed BPF_J{SET,EQ,NE} is actually the
* 3rd instruction. See comments of "gen_j{set,_eq}_64()".
*/
curr_off += 2 * INSN_len_normal;
disp = get_displacement(curr_off, targ_off);
/* There is a "cc" field in the issued instruction. */
return is_valid_near_disp(disp);
case ARC_CC_AL:
disp = get_displacement(curr_off, targ_off);
return is_valid_far_disp(disp);
default:
return false;
}
}
/*
* The template for the 64-bit jumps with the following BPF conditions
*
* u< u<= u> u>= s< s<= s> s>=
*
* Looks like below:
*
* cmp rd_hi, rs_hi
* b<c1> @target
* b<c2> @end
* cmp rd_lo, rs_lo # if execution reaches here, r{d,s}_hi are equal
* b<c3> @target
* end:
*
* "c1" is the condition that JIT is handling minus the equality part.
* For instance if we have to translate an "unsigned greater or equal",
* then "c1" will be "unsigned greater". We won't know about equality
* until all 64-bits of data (higeher and lower registers) are processed.
*
* "c2" is the counter logic of "c1". For instance, if "c1" is originated
* from "s>", then "c2" would be "s<". Notice that equality doesn't play
* a role here either, because the lower 32 bits are not processed yet.
*
* "c3" is the unsigned version of "c1", no matter if the BPF condition
* was signed or unsigned. An unsigned version is necessary, because the
* MSB of the lower 32 bits does not reflect a sign in the whole 64-bit
* scheme. Otherwise, 64-bit comparisons like
* (0x0000_0000,0x8000_0000) s>= (0x0000_0000,0x0000_0000)
* would yield an incorrect result. Finally, if there is an equality
* check in the BPF condition, it will be reflected in "c3".
*
* You can find all the instances of this template where the
* "arcv2_64_jccs" is getting initialised.
*/
static u8 gen_jcc_64(u8 *buf, u8 rd, u8 rs, u8 cond,
u32 curr_off, u32 targ_off)
{
s32 disp;
u32 end_off;
const u8 *cc = arcv2_64_jccs.jmp[cond].cond;
u8 len = 0;
/* cmp rd_hi, rs_hi */
len += arc_cmp_r(buf, REG_HI(rd), REG_HI(rs));
/*
* This function only applies the necessary logic to make the proper
* translations. All the sanity checks must have already been done
* by calling the check_jmp_64().
*/
u8 gen_jmp_64(u8 *buf, u8 rd, u8 rs, u8 cond, u32 curr_off, u32 targ_off)
{
u8 len = 0;
bool eq = false;
s32 disp;
switch (cond) {
case ARC_CC_AL:
disp = get_displacement(curr_off, targ_off);
len = arc_b(buf, disp);
break;
case ARC_CC_UGT:
case ARC_CC_UGE:
case ARC_CC_ULT:
case ARC_CC_ULE:
case ARC_CC_SGT:
case ARC_CC_SGE:
case ARC_CC_SLT:
case ARC_CC_SLE:
len = gen_jcc_64(buf, rd, rs, cond, curr_off, targ_off);
break;
case ARC_CC_EQ:
eq = true;
fallthrough;
case ARC_CC_NE:
len = gen_j_eq_64(buf, rd, rs, eq, curr_off, targ_off);
break;
case ARC_CC_SET:
len = gen_jset_64(buf, rd, rs, curr_off, targ_off);
break;
default:
#ifdef ARC_BPF_JIT_DEBUG
pr_err("64-bit jump condition is not known.");
BUG();
#endif
}
return len;
}
/*
* The condition codes to use when generating JIT instructions
* for 32-bit jumps.
*
* The "ARC_CC_AL" index is not really used by the code, but it
* is here for the sake of completeness.
*
* The "ARC_CC_SET" becomes "CC_unequal" because of the "tst"
* instruction that precedes the conditional branch.
*/
static const u8 arcv2_32_jmps[ARC_CC_LAST] = {
[ARC_CC_UGT] = CC_great_u,
[ARC_CC_UGE] = CC_great_eq_u,
[ARC_CC_ULT] = CC_less_u,
[ARC_CC_ULE] = CC_less_eq_u,
[ARC_CC_SGT] = CC_great_s,
[ARC_CC_SGE] = CC_great_eq_s,
[ARC_CC_SLT] = CC_less_s,
[ARC_CC_SLE] = CC_less_eq_s,
[ARC_CC_AL] = CC_always,
[ARC_CC_EQ] = CC_equal,
[ARC_CC_NE] = CC_unequal,
[ARC_CC_SET] = CC_unequal
};
/* Can the jump from "curr_off" to "targ_off" actually happen? */
bool check_jmp_32(u32 curr_off, u32 targ_off, u8 cond)
{
u8 addendum;
s32 disp;
if (cond >= ARC_CC_LAST)
return false;
/*
* The unconditional jump happens immediately, while the rest
* are either preceded by a "cmp" or "tst" instruction.
*/
addendum = (cond == ARC_CC_AL) ? 0 : INSN_len_normal;
disp = get_displacement(curr_off + addendum, targ_off);
if (cond == ARC_CC_AL)
return is_valid_far_disp(disp);
else
return is_valid_near_disp(disp);
}
/*
* Although this must have already been checked by "check_jmp_32()",
* we're not going to risk accessing "arcv2_32_jmps" array without
* the boundary check.
*/
if (cond >= ARC_CC_LAST) {
#ifdef ARC_BPF_JIT_DEBUG
pr_err("32-bit jump condition is not known.");
BUG();
#endif
return 0;
}
/* If there is a "condition", issue the "cmp" or "tst" first. */
if (cond != ARC_CC_AL) {
if (cond == ARC_CC_SET)
len = tst_r32(buf, rd, rs);
else
len = cmp_r32(buf, rd, rs);
/*
* The issued instruction affects the "disp"lacement as
* it alters the "curr_off" by its "len"gth. The "curr_off"
* should always point to the jump instruction.
*/
disp = get_displacement(curr_off + len, targ_off);
len += arc_bcc(BUF(buf, len), arcv2_32_jmps[cond], disp);
} else {
/* The straight forward unconditional jump. */
disp = get_displacement(curr_off, targ_off);
len = arc_b(buf, disp);
}
return len;
}
/*
* Generate code for functions calls. There can be two types of calls:
*
* - Calling another BPF function
* - Calling an in-kernel function which is compiled by ARC gcc
*
* In the later case, we must comply to ARCv2 ABI and handle arguments
* and return values accordingly.
*/
u8 gen_func_call(u8 *buf, ARC_ADDR func_addr, bool external_func)
{
u8 len = 0;
/*
* In case of an in-kernel function call, always push the 5th
* argument onto the stack, because that's where the ABI dictates
* it should be found. If the callee doesn't really use it, no harm
* is done. The stack is readjusted either way after the call.
*/
if (external_func)
len += push_r64(BUF(buf, len), BPF_REG_5);
len += jump_and_link(BUF(buf, len), func_addr);
if (external_func)
len += arc_add_i(BUF(buf, len), ARC_R_SP, ARC_R_SP, ARG5_SIZE);
return len;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.96 Sekunden
(vorverarbeitet am 2026-10-12)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.