diff options
| author | Tengda Wu <wutengda@huaweicloud.com> | 2026-10-20 13:44:58 +0000 |
|---|---|---|
| committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2026-10-22 17:17:01 -0300 |
| commit | 059f12f8cd29adcf59973a8ea4ca96ea4873fcf0 (patch) | |
| tree | 03b1ac7a58e2ecd6aa0d28a3355ce3ec6f914684 /tools | |
| parent | edc11f5794424988314966df3bde1d8573972cd7 (diff) | |
| download | linux-059f12f8cd29adcf59973a8ea4ca96ea4873fcf0.tar.gz linux-059f12f8cd29adcf59973a8ea4ca96ea4873fcf0.zip | |
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 <irogers@google.com>
Signed-off-by: Li Huafei <lihuafei1@huawei.com>
Signed-off-by: Tengda Wu <wutengda@huaweicloud.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/perf/util/annotate-arch/annotate-arm64.c | 87 | ||||
| -rw-r--r-- | tools/perf/util/annotate-data.c | 15 |
2 files changed, 101 insertions, 1 deletions
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 <regex.h> #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 @@ check_modifiers: } } +#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 ?: "<unknown>"); + + /* 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 @@ out: 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; } |
