mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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>
This commit is contained in:
parent
edc11f5794
commit
059f12f8cd
|
|
@ -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 @@ 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 ?: "<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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user