From 143ad11b37630c89b5fe19f521cc7ebeda0479ee Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Tue, 20 Oct 2026 13:45:08 +0000 Subject: [PATCH] perf annotate-arm64: Support 'add' instruction tracking Extend update_insn_state_arm64() to track 'add' instructions for structure member address calculation. Unlike x86, the arm64 'add' instruction has an extra base register among its source operands. Therefore, in terms of propagating the data type, it is essentially performing a 'mov', except that before the 'mov', it first needs to be updated by adding the offset or reg2. A real-world example is shown below: ffff80008001c9a8 : ffff80008001c9c4: add x19, x0, #0xeb8 // x0 (task_struct*) + 0xeb8 -> x19 * ffff80008001c9d0: ldr x0, [x19] Before this commit, the type flow broke at the 'add' instruction, leaving the subsequent load with no type information: chk [28] reg19 offset=0 ok=0 kind=0 cfa : no type information final result: no type information After this commit, the tracker correctly follows the member address calculation: var [0] reg0 offset 0 type='struct task_struct*' add [1c] address of 0xeb8(reg0) -> reg19 type='struct task_struct*' chk [28] reg19 offset=0 ok=1 kind=1 (struct task_struct*) : Good! found by insn track: 0(reg19) type-offset=0xeb8 final result: type='struct task_struct' Reviewed-by: Ian Rogers Reviewed-by: Namhyung Kim Signed-off-by: Tengda Wu Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/util/annotate-arch/annotate-arm64.c | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c index 38a9cb37c952..61df9c4f4877 100644 --- a/tools/perf/util/annotate-arch/annotate-arm64.c +++ b/tools/perf/util/annotate-arch/annotate-arm64.c @@ -959,6 +959,74 @@ static void update_mov_insn_state(struct type_state *state, pr_debug_type_name(&tsr->type, tsr->kind); } +static void update_add_insn_state(struct type_state *state, + struct data_loc_info *dloc, + struct disasm_line *dl, + struct annotated_op_loc *src, + struct annotated_op_loc *dst) +{ + struct type_state_reg *tsr; + struct type_state_reg src_tsr; + u32 insn_offset = dl->al.offset; + int sreg = src->reg1; + int dreg = dst->reg1; + int reg_offset; + + if (!has_reg_type(state, dreg)) + return; + + tsr = &state->regs[dreg]; + tsr->copied_from = -1; + tsr->lifetime_active = false; + tsr->lifetime_end = 0; + +retry: + if (!has_reg_type(state, sreg) || !state->regs[sreg].ok) { + invalidate_reg_state(tsr); + return; + } + + src_tsr = state->regs[sreg]; + + if (arch_get_reg_offset(dloc->arch, src, sreg, state, false, ®_offset)) + goto add_non_regoff; + + if (src_tsr.kind == TSR_KIND_CONST) { + tsr->kind = src_tsr.kind; + tsr->imm_value = src_tsr.imm_value + reg_offset; + tsr->offset = 0; + tsr->ok = src_tsr.ok; + + pr_debug_dtp("add [%x] imm %#x(reg%d) -> reg%d\n", + insn_offset, reg_offset, sreg, dreg); + return; + } + + if (src_tsr.kind == TSR_KIND_POINTER || + (src_tsr.kind == TSR_KIND_TYPE && + dwarf_tag(&src_tsr.type) == DW_TAG_pointer_type)) { + tsr->type = src_tsr.type; + tsr->kind = src_tsr.kind; + tsr->imm_value = src_tsr.imm_value; + tsr->offset = src_tsr.offset + reg_offset; + tsr->ok = src_tsr.ok; + + pr_debug_dtp("add [%x] address of %#x(reg%d) -> reg%d", + insn_offset, reg_offset, sreg, dreg); + pr_debug_type_name(&tsr->type, tsr->kind); + return; + } + +add_non_regoff: + /* Try another register if any */ + if (src->multi_regs && src->reg1 != src->reg2 && sreg != src->reg2 && + !(src->extend_type || src->shift_type)) { + sreg = src->reg2; + goto retry; + } + invalidate_reg_state(tsr); +} + static void update_insn_state_arm64(struct type_state *state, struct data_loc_info *dloc, Dwarf_Die *cu_die, struct disasm_line *dl) @@ -975,6 +1043,8 @@ static void update_insn_state_arm64(struct type_state *state, if (ins__is_call(&dl->ins)) update_call_insn_state(state, dloc, cu_die, dl); + else if (!strcmp(dl->ins.name, "add")) + update_add_insn_state(state, dloc, dl, src, dst); /* Register to register or imm value to register transfers */ else if (!strcmp(dl->ins.name, "mov")) update_mov_insn_state(state, dl, src, dst);