mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 02:21:39 -04:00
arm64: insn: Add encoder for ADD/SUB (extended register)
The insn library encodes the immediate and shifted-register forms of ADD/SUB but not the extended-register form. The BPF JIT wants it to rebase a 32-bit arena offset onto the arena kernel base in a single instruction, add xN, xBASE, wN, uxtw, instead of a separate zero-extend followed by a plain add. Add aarch64_insn_gen_add_sub_extended_reg(), modeled on the shifted-register generator. The option and imm3 fields occupy the same bits as the shifted form's shift amount, so they are encoded through the existing IMM_6 field type. The opt field in bits 23:22 is part of the opcode here rather than a shift type, and any value other than 00 is unallocated, so the decode masks cover it. Note that register 31 does not mean the same thing in the two forms: in the extended-register encoding it is SP for Rn, and for Rd unless the instruction sets the flags, while it stays XZR for Rm. Callers porting a shifted-register site that passes A64_ZR need to be aware of that, so say so above the function. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Puranjay Mohan <puranjay@kernel.org> Reviewed-by: Xu Kuohai <xukuohai@huawei.com> Link: https://lore.kernel.org/bpf/20260813190356.335181-3-puranjay@kernel.org Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
committed by
Kumar Kartikeya Dwivedi
parent
50de1c47a4
commit
f4adb983ef
@@ -205,6 +205,18 @@ enum aarch64_insn_adsb_type {
|
||||
AARCH64_INSN_ADSB_SUB_SETFLAGS
|
||||
};
|
||||
|
||||
/* option field of add/sub (extended register) */
|
||||
enum aarch64_insn_extend_type {
|
||||
AARCH64_INSN_EXTEND_UXTB,
|
||||
AARCH64_INSN_EXTEND_UXTH,
|
||||
AARCH64_INSN_EXTEND_UXTW,
|
||||
AARCH64_INSN_EXTEND_UXTX,
|
||||
AARCH64_INSN_EXTEND_SXTB,
|
||||
AARCH64_INSN_EXTEND_SXTH,
|
||||
AARCH64_INSN_EXTEND_SXTW,
|
||||
AARCH64_INSN_EXTEND_SXTX,
|
||||
};
|
||||
|
||||
enum aarch64_insn_movewide_type {
|
||||
AARCH64_INSN_MOVEWIDE_ZERO,
|
||||
AARCH64_INSN_MOVEWIDE_KEEP,
|
||||
@@ -378,6 +390,10 @@ __AARCH64_INSN_FUNCS(add, 0x7F200000, 0x0B000000)
|
||||
__AARCH64_INSN_FUNCS(adds, 0x7F200000, 0x2B000000)
|
||||
__AARCH64_INSN_FUNCS(sub, 0x7F200000, 0x4B000000)
|
||||
__AARCH64_INSN_FUNCS(subs, 0x7F200000, 0x6B000000)
|
||||
__AARCH64_INSN_FUNCS(add_ext, 0x7FE00000, 0x0B200000)
|
||||
__AARCH64_INSN_FUNCS(adds_ext, 0x7FE00000, 0x2B200000)
|
||||
__AARCH64_INSN_FUNCS(sub_ext, 0x7FE00000, 0x4B200000)
|
||||
__AARCH64_INSN_FUNCS(subs_ext, 0x7FE00000, 0x6B200000)
|
||||
__AARCH64_INSN_FUNCS(madd, 0x7FE08000, 0x1B000000)
|
||||
__AARCH64_INSN_FUNCS(msub, 0x7FE08000, 0x1B008000)
|
||||
__AARCH64_INSN_FUNCS(udiv, 0x7FE0FC00, 0x1AC00800)
|
||||
@@ -637,6 +653,13 @@ u32 aarch64_insn_gen_add_sub_shifted_reg(enum aarch64_insn_register dst,
|
||||
int shift,
|
||||
enum aarch64_insn_variant variant,
|
||||
enum aarch64_insn_adsb_type type);
|
||||
u32 aarch64_insn_gen_add_sub_extended_reg(enum aarch64_insn_register dst,
|
||||
enum aarch64_insn_register src,
|
||||
enum aarch64_insn_register reg,
|
||||
enum aarch64_insn_extend_type extend,
|
||||
int shift,
|
||||
enum aarch64_insn_variant variant,
|
||||
enum aarch64_insn_adsb_type type);
|
||||
u32 aarch64_insn_gen_data1(enum aarch64_insn_register dst,
|
||||
enum aarch64_insn_register src,
|
||||
enum aarch64_insn_variant variant,
|
||||
|
||||
@@ -986,6 +986,66 @@ u32 aarch64_insn_gen_add_sub_shifted_reg(enum aarch64_insn_register dst,
|
||||
return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlike the shifted-register form, register 31 is not XZR everywhere here:
|
||||
* it encodes SP for @src, and for @dst too unless @type sets the flags. Only
|
||||
* @reg keeps the XZR meaning.
|
||||
*/
|
||||
u32 aarch64_insn_gen_add_sub_extended_reg(enum aarch64_insn_register dst,
|
||||
enum aarch64_insn_register src,
|
||||
enum aarch64_insn_register reg,
|
||||
enum aarch64_insn_extend_type extend,
|
||||
int shift,
|
||||
enum aarch64_insn_variant variant,
|
||||
enum aarch64_insn_adsb_type type)
|
||||
{
|
||||
u32 insn;
|
||||
|
||||
switch (type) {
|
||||
case AARCH64_INSN_ADSB_ADD:
|
||||
insn = aarch64_insn_get_add_ext_value();
|
||||
break;
|
||||
case AARCH64_INSN_ADSB_SUB:
|
||||
insn = aarch64_insn_get_sub_ext_value();
|
||||
break;
|
||||
case AARCH64_INSN_ADSB_ADD_SETFLAGS:
|
||||
insn = aarch64_insn_get_adds_ext_value();
|
||||
break;
|
||||
case AARCH64_INSN_ADSB_SUB_SETFLAGS:
|
||||
insn = aarch64_insn_get_subs_ext_value();
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: unknown add/sub encoding %d\n", __func__, type);
|
||||
return AARCH64_BREAK_FAULT;
|
||||
}
|
||||
|
||||
switch (variant) {
|
||||
case AARCH64_INSN_VARIANT_32BIT:
|
||||
break;
|
||||
case AARCH64_INSN_VARIANT_64BIT:
|
||||
insn |= AARCH64_INSN_SF_BIT;
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: unknown variant encoding %d\n", __func__, variant);
|
||||
return AARCH64_BREAK_FAULT;
|
||||
}
|
||||
|
||||
if (shift < 0 || shift > 4) {
|
||||
pr_err("%s: invalid shift encoding %d\n", __func__, shift);
|
||||
return AARCH64_BREAK_FAULT;
|
||||
}
|
||||
|
||||
insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
|
||||
|
||||
insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
|
||||
|
||||
insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg);
|
||||
|
||||
/* option in bits [15:13] and imm3 in [12:10] together fill IMM_6 */
|
||||
return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn,
|
||||
(extend << 3) | shift);
|
||||
}
|
||||
|
||||
u32 aarch64_insn_gen_data1(enum aarch64_insn_register dst,
|
||||
enum aarch64_insn_register src,
|
||||
enum aarch64_insn_variant variant,
|
||||
|
||||
Reference in New Issue
Block a user