perf annotate-arm64: Support stack variable tracking

Extend update_insn_state_arm64() to track data types stored on the
stack.

The implementation handles:
1. Stack Loads: Identify when a register is loaded from a stack
   slot and update the register's type state based on the tracked
   stack content or compound member types.
2. Stack Stores: Update or create new stack state entries when
   a tracked register type is stored to the stack.

Similar to loads, stores also support saving one or two registers
(in the case of 'stp'). Therefore, propagate_store_reg_state() is
introduced.

With these changes, the instruction tracker can now follow data types
as they move between registers and memory, specifically for function
local variables and compiler-spilled values on arm64.

Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Tengda Wu <wutengda@huaweicloud.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Tengda Wu 2026-10-20 13:45:06 +00:00 committed by Arnaldo Carvalho de Melo
parent aa2cd2e19c
commit 861ecd5c6c

View File

@ -624,6 +624,8 @@ static int propagate_load_reg_state(struct type_state *state,
u32 insn_offset = dl->al.offset;
int sreg = src->reg1;
int reg_offset;
int fbreg = dloc->fbreg;
int fboff = 0;
if (!has_reg_type(state, dreg))
return -1;
@ -633,12 +635,57 @@ static int propagate_load_reg_state(struct type_state *state,
tsr->lifetime_active = false;
tsr->lifetime_end = 0;
if (dloc->fb_cfa) {
u64 ip = dloc->ms->sym->start + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
fbreg = -1;
}
retry:
if (arch_get_reg_offset(dloc->arch, src, sreg, state, false, &reg_offset))
goto load_non_regoff;
reg_offset += mem_spacing;
/* Check stack variables with offset */
if (sreg != -1 && (sreg == fbreg || sreg == state->stack_reg)) {
struct type_state_stack *stack;
int offset = sreg == fbreg ? reg_offset - fboff : reg_offset;
stack = find_stack_state(state, offset);
if (stack == NULL) {
return -1;
} else if (!stack->compound) {
tsr->type = stack->type;
tsr->kind = stack->kind;
tsr->offset = stack->ptr_offset;
tsr->imm_value = stack->imm_value;
tsr->ok = true;
} else if (die_get_member_type(&stack->type,
offset - stack->offset,
&type_die)) {
tsr->type = type_die;
tsr->kind = TSR_KIND_TYPE;
tsr->offset = 0;
tsr->imm_value = 0;
tsr->ok = true;
} else {
return -1;
}
if (sreg == fbreg) {
pr_debug_dtp("%s [%x] -%#x(stack) -> reg%d",
dl->ins.name, insn_offset, -offset, dreg);
} else {
pr_debug_dtp("%s [%x] %#x(reg%d) -> reg%d",
dl->ins.name, insn_offset, offset, sreg, dreg);
}
pr_debug_type_name(&tsr->type, tsr->kind);
return 0;
}
if (!src_tsr || !src_tsr->ok)
return -1;
@ -757,18 +804,108 @@ static bool is_standard_store_insn(const char *name)
!strncmp(name, "stp", 3); /* stp */
}
/*
* For store insns: propagate type from @sreg to the memory location referenced
* by @dst, applying @mem_spacing to account for multi-regs stores (e.g. stp).
*/
static void propagate_store_reg_state(struct type_state *state,
struct data_loc_info *dloc,
struct disasm_line *dl, int sreg,
struct annotated_op_loc *dst,
int mem_spacing)
{
struct type_state_reg *tsr;
u32 insn_offset = dl->al.offset;
int dreg = dst->reg1;
int reg_offset;
int fbreg = dloc->fbreg;
int fboff = 0;
if (arch_get_reg_offset(dloc->arch, dst, dreg, state, false, &reg_offset))
return;
reg_offset += mem_spacing;
if (dloc->fb_cfa) {
u64 ip = dloc->ms->sym->start + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
fbreg = -1;
}
/* Check stack variables with offset */
if (dreg != -1 && (dreg == fbreg || dreg == state->stack_reg)) {
struct type_state_stack *stack;
int offset = dreg == fbreg ? reg_offset - fboff : reg_offset;
if (!has_reg_type(state, sreg) || !state->regs[sreg].ok) {
stack = find_stack_state(state, offset);
/* Preserve compound states when only a member is overwritten. */
if (stack && !stack->compound)
delete_stack_state(state, offset);
return;
}
tsr = &state->regs[sreg];
stack = find_stack_state(state, offset);
if (stack) {
if (!stack->compound)
set_stack_state(stack, offset, tsr->kind, &tsr->type,
tsr->offset, tsr->imm_value);
/*
* If it's a compound type, it means attempting to
* write to a member value of the compound type without
* changing the compound type itself, so do nothing.
*/
} else {
findnew_stack_state(state, offset, tsr->kind, &tsr->type,
tsr->offset, tsr->imm_value);
}
if (dreg == fbreg) {
pr_debug_dtp("%s [%x] reg%d -> -%#x(stack)",
dl->ins.name, insn_offset, sreg, -offset);
} else {
pr_debug_dtp("%s [%x] reg%d -> %#x(reg%d)",
dl->ins.name, insn_offset, sreg, offset, dreg);
}
if (tsr->offset != 0)
pr_debug_dtp(" reg%d offset %#x ->", sreg, tsr->offset);
pr_debug_type_name(&tsr->type, tsr->kind);
}
/*
* Ignore other transfers since it'd set a value in a struct
* and won't change the type.
*/
}
static void update_store_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)
{
/*
* Ignore transfers since it'd set a value in a struct
* and won't change the type.
*
* Needs to update the pre-index and post-index addressing
* modes for the destination register.
*/
int mem_spacing = 0;
if (!has_reg_type(state, dst->reg1))
return;
/* Handle the first source register */
propagate_store_reg_state(state, dloc, dl, src->reg1, dst, mem_spacing);
/* Handle the second source register (if any) */
if (src->multi_regs) {
mem_spacing = arm64__reg_size(dl->ops.source.raw);
if (mem_spacing >= 0)
propagate_store_reg_state(state, dloc, dl, src->reg2,
dst, mem_spacing);
}
adjust_reg_index_state(state, dloc, dl, dst);
}
@ -793,7 +930,7 @@ static void update_insn_state_arm64(struct type_state *state,
update_load_insn_state(state, dloc, dl, src, dst);
/* Register to memory transfers */
else if (is_standard_store_insn(dl->ins.name))
update_store_insn_state(state, dloc, dl, dst);
update_store_insn_state(state, dloc, dl, src, dst);
else
invalidate_unhandled_insn_regs(state, dl, src, dst);
}