From 059f12f8cd29adcf59973a8ea4ca96ea4873fcf0 Mon Sep 17 00:00:00 2001 From: Tengda Wu Date: Tue, 20 Oct 2026 13:44:58 +0000 Subject: [PATCH] perf annotate-arm64: Enable instruction tracking support Enable basic instruction tracking for arm64 by implementing three essential functions in the find_data_type_block() call path: find_data_type_block -> arch_supports_insn_tracking (1) -> find_data_type_insn -> init_type_state (2) -> update_var_state -> update_insn_state (3) Changes: * arch_supports_insn_tracking(): add arm64 to the list of supported architectures, allowing find_data_type_block() to proceed with data type analysis. * init_type_state(): correctly identify ret_reg, stack_reg, and caller-saved registers for arm64 during type state initialization. * update_insn_state(): add the update_insn_state_arm64() callback with conservative register invalidation for call instructions and those with destination register(s), preventing stale type propagation. For now the invalidation path does not treat special instructions individually; doing so for every such instruction up front would be costly. A TODO is therefore left in invalidate_unhandled_insn_regs() marking where per-instruction invalidation handling is to be extended in future patches. With these changes, arm64 gains support for basic variable type inference during instruction tracking. Reviewed-by: Ian Rogers Signed-off-by: Li Huafei Signed-off-by: Tengda Wu Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/util/annotate-arch/annotate-arm64.c | 87 +++++++++++++++++++ tools/perf/util/annotate-data.c | 15 +++- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c index 6549535961a9..d591d556ad40 100644 --- a/tools/perf/util/annotate-arch/annotate-arm64.c +++ b/tools/perf/util/annotate-arch/annotate-arm64.c @@ -9,6 +9,10 @@ #include #include "../annotate.h" #include "../disasm.h" +#include "../annotate-data.h" +#include "../debug.h" +#include "../map.h" +#include "../symbol.h" struct arch_arm64 { struct arch arch; @@ -448,6 +452,86 @@ static void extract_op_location_arm64(const struct arch *arch, } } +#ifdef HAVE_LIBDW_SUPPORT +/* Check if the instruction is read-only (does not modify register state). */ +static bool is_readonly_insn(const char *name) +{ + return !strncmp(name, "cb", 2) || !strncmp(name, "tb", 2) || + !strcmp(name, "tst") || !strcmp(name, "teq") || + !strcmp(name, "cmp") || !strcmp(name, "cmn") || + !strcmp(name, "ccmp") || !strcmp(name, "ccmn"); +} + +static void update_call_insn_state(struct type_state *state, + struct data_loc_info *dloc, + struct disasm_line *dl) +{ + struct symbol *func = dl->ops.target.sym; + const char *call_name; + u64 call_addr; + u32 insn_offset = dl->al.offset; + + call_name = func ? func->name : dl->ops.target.name; + pr_debug_dtp("call [%x] %s\n", insn_offset, call_name ?: ""); + + /* Invalidate caller-saved registers after call */ + call_addr = map__rip_2objdump(dloc->ms->map, + dloc->ms->sym->start + dl->al.offset); + for (unsigned int i = 0; i < ARRAY_SIZE(state->regs); i++) { + struct type_state_reg *reg = &state->regs[i]; + + if (!reg->caller_saved) + continue; + /* Keep register valid within DWARF location lifetime */ + if (reg->lifetime_active && call_addr < reg->lifetime_end) + continue; + invalidate_reg_state(reg); + } +} + +/* Invalidate registers explicitly written by an unhandled instruction. */ +static void invalidate_unhandled_insn_regs(struct type_state *state, + struct disasm_line *dl, + struct annotated_op_loc *dst) +{ + u32 insn_offset = dl->al.offset; + + /* Invalidate destination registers */ + if (has_reg_type(state, dst->reg1) && !dst->mem_ref) { + pr_debug_dtp("%s [%x] invalidate reg%d", + dl->ins.name, insn_offset, dst->reg1); + invalidate_reg_state(&state->regs[dst->reg1]); + + if (dst->multi_regs && has_reg_type(state, dst->reg2)) { + pr_debug_dtp(" and reg%d", dst->reg2); + invalidate_reg_state(&state->regs[dst->reg2]); + } + pr_debug_dtp("\n"); + } + + /* TODO: Extend invalidation support to more special instructions. */ +} + +static void update_insn_state_arm64(struct type_state *state, + struct data_loc_info *dloc, Dwarf_Die *cu_die __maybe_unused, + struct disasm_line *dl) +{ + struct annotated_insn_loc loc; + struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET]; + + if (is_readonly_insn(dl->ins.name)) + return; + + if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0) + return; + + if (ins__is_call(&dl->ins)) + update_call_insn_state(state, dloc, dl); + else + invalidate_unhandled_insn_regs(state, dl, dst); +} +#endif + const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id, const char *cpuid __maybe_unused) { @@ -467,6 +551,9 @@ const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id, arch->objdump.imm_char = '#'; arch->associate_instruction_ops = arm64__associate_instruction_ops; arch->extract_op_location = extract_op_location_arm64; +#ifdef HAVE_LIBDW_SUPPORT + arch->update_insn_state = update_insn_state_arm64; +#endif /* bl, blr */ err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED); diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 32af1be4e451..398795ba3694 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -29,6 +29,7 @@ /* register number of the stack pointer */ #define X86_REG_SP 7 +#define ARM64_REG_SP 31 static void delete_var_types(struct die_var_type *var_types); @@ -179,6 +180,17 @@ static void init_type_state(struct type_state *state, const struct arch *arch) state->regs[11].caller_saved = true; state->ret_reg = 0; state->stack_reg = X86_REG_SP; + } else if (arch__is_arm64(arch)) { + /* + * Mark x0..x18 as caller-saved. x30 (LR) is intentionally + * omitted from the caller_saved set: it is clobbered by + * calls, but holds only the return address and is not used + * to carry data type information. + */ + for (int i = 0; i < 19; i++) + state->regs[i].caller_saved = true; + state->ret_reg = 0; + state->stack_reg = ARM64_REG_SP; } } @@ -1543,7 +1555,8 @@ static enum type_match_result find_data_type_insn(struct data_loc_info *dloc, static int arch_supports_insn_tracking(struct data_loc_info *dloc) { - if ((arch__is_x86(dloc->arch)) || (arch__is_powerpc(dloc->arch))) + if (arch__is_x86(dloc->arch) || arch__is_powerpc(dloc->arch) || + arch__is_arm64(dloc->arch)) return 1; return 0; }