diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c index e0bb37d8b353..e97a0cbe02fc 100644 --- a/tools/perf/util/annotate-arch/annotate-arm64.c +++ b/tools/perf/util/annotate-arch/annotate-arm64.c @@ -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, ®_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, ®_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); }