[PATCH v5 23/26] perf annotate-arm64: Support 'add' instruction tracking
From: Tengda Wu
Date: Tue Sep 08 2026 - 09:30:57 EST
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 <flush_ptrace_hw_breakpoint>:
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'
Signed-off-by: Tengda Wu <wutengda@xxxxxxxxxxxxxxx>
---
.../perf/util/annotate-arch/annotate-arm64.c | 72 ++++++++++++++++++-
1 file changed, 70 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c
index 2f8bedf583c1..e237b9142684 100644
--- a/tools/perf/util/annotate-arch/annotate-arm64.c
+++ b/tools/perf/util/annotate-arch/annotate-arm64.c
@@ -864,6 +864,72 @@ 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;
+
+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 try_other_reg;
+
+ 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;
+ }
+
+try_other_reg:
+ /* 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)
@@ -930,7 +996,7 @@ static void update_insn_state_arm64(struct type_state *state,
* prevent stale type info from propagating to subsequent instructions.
*/
if (has_reg_type(state, dst->reg1) && !dst->mem_ref &&
- strcmp(dl->ins.name, "mov") &&
+ strcmp(dl->ins.name, "add") && strcmp(dl->ins.name, "mov") &&
!is_standard_load_insn(dl->ins.name)) {
pr_debug_dtp("%s [%x] invalidate reg%d",
dl->ins.name, insn_offset, dst->reg1);
@@ -943,8 +1009,10 @@ static void update_insn_state_arm64(struct type_state *state,
return;
}
+ if (!strcmp(dl->ins.name, "add"))
+ update_add_insn_state(state, dloc, dl, src, dst);
/* Register to register or imm value to register transfers */
- if (!strcmp(dl->ins.name, "mov"))
+ else if (!strcmp(dl->ins.name, "mov"))
update_mov_insn_state(state, dl, src, dst);
/* Memory to register transfers */
else if (is_standard_load_insn(dl->ins.name))
--
2.34.1