Linux Perf Users
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Tengda Wu" <wutengda@huaweicloud.com>
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [PATCH v8 05/15] perf annotate-arm64: Support load instruction tracking
Date: Tue, 20 Oct 2026 14:36:34 +0000	[thread overview]
Message-ID: <sashiko-outbox-179159@kernel.org> (raw)
In-Reply-To: <a6085b6f08ba2ed533208ebe9227968770cd58ca.1792411435.git.wutengda@huaweicloud.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] perf annotate-arm64: unused type_die in propagate_load_reg_state()
- [Medium] perf annotate-arm64: missing reg2 extraction in ldp when reg1 is xzr/wzr
--

commit d4331cee155820a433821da388de2a7297699e0e
Author: Tengda Wu <wutengda@huaweicloud.com>

perf annotate-arm64: Support load instruction tracking

This commit extends update_insn_state_arm64() to track register state
changes across standard load and load-pair instructions, including
pre-index and post-index addressing modes.

> diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c
> index c4cc8bca85cc0..0b5d1abbc5755 100644
> --- a/tools/perf/util/annotate-arch/annotate-arm64.c
> +++ b/tools/perf/util/annotate-arch/annotate-arm64.c
> @@ -527,11 +527,212 @@ static void invalidate_unhandled_insn_regs(struct type_state *state,
>  	/* TODO: Extend invalidation support to more special instructions. */
>  }
>  
> +static int arm64__reg_size(const char *reg)
> +{
> +	if (!reg || !*reg || !arm64__is_reg(reg))
> +		return -1;
> +
> +	if (reg[0] == 'w')
> +		return 4;
> +
> +	if (reg[0] == 'x' || !strncmp(reg, "sp", 2))
> +		return 8;
> +
> +	return -1;
> +}

[ ... ]

> +static int propagate_load_reg_state(struct type_state *state,
> +				    struct data_loc_info *dloc,
> +				    struct disasm_line *dl, int dreg,
> +				    struct annotated_op_loc *src,
> +				    struct type_state_reg **src_states,
> +				    int mem_spacing)
> +{
> +	struct type_state_reg *tsr;
> +	struct type_state_reg *src_tsr = src_states[0];
> +	Dwarf_Die type_die;
> +	u32 insn_offset = dl->al.offset;
> +	int sreg = src->reg1;
> +	int reg_offset;
> +
> +	if (!has_reg_type(state, dreg))
> +		return -1;
> +
> +	tsr = &state->regs[dreg];
> +	tsr->copied_from = -1;
> +	tsr->lifetime_active = false;
> +	tsr->lifetime_end = 0;
> +
> +retry:
> +	if (arch_get_reg_offset(dloc->arch, src, sreg, state, false, &reg_offset))
> +		goto load_non_regoff;
> +
> +	reg_offset += mem_spacing;
> +
> +	if (!src_tsr || !src_tsr->ok)
> +		return -1;
> +
> +	/* Dereference the pointer if it has one */
> +	if (src_tsr->kind == TSR_KIND_TYPE &&
> +	    die_deref_ptr_type(&src_tsr->type,
> +			       src_tsr->offset + reg_offset, &type_die)) {
> +		tsr->type = type_die;
> +		tsr->kind = TSR_KIND_TYPE;
> +		tsr->offset = 0;
> +		tsr->ok = true;
> +
> +		pr_debug_dtp("%s [%x] %#x(reg%d) -> reg%d",
> +			     dl->ins.name, insn_offset, reg_offset, sreg, dreg);
> +		pr_debug_type_name(&tsr->type, tsr->kind);
> +		return 0;
> +	}
> +
> +	/* Handle dereference of TSR_KIND_POINTER registers */
> +	if (src_tsr->kind == TSR_KIND_POINTER &&
> +	    die_get_member_type(&src_tsr->type,
> +				src_tsr->offset + reg_offset, &type_die)) {
> +		tsr->type = src_tsr->type;
> +		tsr->kind = TSR_KIND_TYPE;
> +		tsr->offset = src_tsr->offset + reg_offset;

[Severity: Medium]
Should tsr->type be assigned type_die and tsr->offset set to 0 here?

When DWARF describes a struct variable via a register-based memory
location (where var->is_reg_var_addr is true), update_var_state() sets
the base register to TSR_KIND_POINTER with reg->type set to the struct
DIE.

When a load instruction reads a member from that struct into dreg,
update_insn_state_arm64() -> update_load_insn_state() ->
propagate_load_reg_state() calls die_get_member_type() to resolve the
loaded member's type into type_die.

However, type_die is left unused, and tsr->type is set to src_tsr->type
(the outer struct DIE) with tsr->offset set to src_tsr->offset +
reg_offset and tsr->kind set to TSR_KIND_TYPE. When a subsequent
instruction dereferences dreg, check_matching_type() or
die_deref_ptr_type() inspects dreg->type, sees the outer struct type
instead of the member's pointer type, and fails with PERF_TMR_NO_POINTER.

> +		tsr->imm_value = 0;
> +		tsr->ok = true;
> +
> +		pr_debug_dtp("%s [%x] addr %#x(reg%d) -> reg%d",
> +			     dl->ins.name, insn_offset, reg_offset, sreg, dreg);
> +		pr_debug_type_name(&tsr->type, tsr->kind);
> +		return 0;
> +	}
> +
> +load_non_regoff:
> +	/* Try another register if any */
> +	if (src->multi_regs && src->reg1 != src->reg2 && sreg != src->reg2 &&
> +	    !(src->extend_type || src->shift_type)) {
> +		sreg = src->reg2;
> +		src_tsr = src_states[1];
> +		goto retry;
> +	}
> +
> +	return -1;
> +}
> +
> +static void update_load_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)
> +{
> +	struct type_state_reg snapshots[2];
> +	struct type_state_reg *src_states[2] = {};
> +	int mem_spacing = 0;
> +
> +	if (!src->mem_ref ||  /* exclude PC-relative loads */
> +	    !has_reg_type(state, src->reg1)) {
> +		/* Invalidate tracked destination registers */
> +		if (has_reg_type(state, dst->reg1))
> +			invalidate_reg_state(&state->regs[dst->reg1]);
> +		if (dst->multi_regs && has_reg_type(state, dst->reg2))
> +			invalidate_reg_state(&state->regs[dst->reg2]);
> +		goto out_adjust;
> +	}
> +
> +	/*
> +	 * Snapshot source register states before updating destination registers.
> +	 * This preserves the original source state for aliased loads (e.g.,
> +	 * ldp x0, x1, [x0]), where updating the first destination may overwrite
> +	 * the source register.
> +	 */
> +	snapshots[0] = state->regs[src->reg1];
> +	src_states[0] = &snapshots[0];
> +	if (src->multi_regs && has_reg_type(state, src->reg2)) {
> +		snapshots[1] = state->regs[src->reg2];
> +		src_states[1] = &snapshots[1];
> +	}
> +
> +	/* Handle the first destination register */
> +	if (has_reg_type(state, dst->reg1)) {
> +		if (propagate_load_reg_state(state, dloc, dl, dst->reg1,
> +					     src, src_states, mem_spacing))
> +			invalidate_reg_state(&state->regs[dst->reg1]);
> +	}
> +
> +	if (!dst->multi_regs || !has_reg_type(state, dst->reg2))
> +		goto out_adjust;

[Severity: Medium]
What happens here when an ldp instruction uses the zero register (xzr or
wzr) as its first destination operand, such as "ldp xzr, x19, [sp], #16"
or "ldp xzr, x1, [x0]"?

In update_load_insn_state(), dst->reg1 is guarded by has_reg_type(state,
dst->reg1) without returning early, and arm64__reg_size() recognizes
xzr/wzr via arm64__is_reg() so dst->reg2 can still be updated or
invalidated.

However, when update_insn_state_arm64() calls
annotate_get_insn_location() -> extract_op_location_arm64(),
arch__dwarf_regnum() returns -1 for xzr and wzr, and
extract_op_location_arm64() returns before parsing reg2:

tools/perf/util/annotate-arch/annotate-arm64.c:extract_op_location_arm64() {
	...
	/* Extract the primary register */
	op_loc->reg1 = arch__dwarf_regnum(arch, s);
	if (op_loc->reg1 == -1)
		return;

	s = next_operand_field(s);
	if (s == NULL)
		return;

	/* Extract secondary register or immediate offset */
	if (op_loc->multi_regs) {
		op_loc->reg2 = arch__dwarf_regnum(arch, s);
	...
}

Doesn't this leave dst->reg2 as -1, causing update_load_insn_state() (and
invalidate_unhandled_insn_regs()) to skip the second register instead of
propagating its type or invalidating its previous state?

> +
> +	/* Handle the second destination register */
> +	if (!strcmp(dl->ins.name, "ldpsw"))
> +		mem_spacing = 4;  /* ldpsw loads two 32-bit signed words (4-byte spacing) */
> +	else
> +		mem_spacing = arm64__reg_size(dl->ops.target.raw);
> +
> +	if (mem_spacing < 0 ||
> +	    propagate_load_reg_state(state, dloc, dl, dst->reg2,
> +				     src, src_states, mem_spacing))
> +		invalidate_reg_state(&state->regs[dst->reg2]);
> +
> +out_adjust:
> +	adjust_reg_index_state(state, dloc, dl, src);
> +}

[ ... ]

>

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1792411435.git.wutengda@huaweicloud.com?part=5

  reply	other threads:[~2026-10-20 14:36 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-20 13:44 [PATCH v8 00/15] perf annotate arm64: Support instruction tracking for data type profiling Tengda Wu
2026-10-20 13:44 ` [PATCH v8 01/15] perf annotate-data: Extract invalidate_reg_state() as a common helper Tengda Wu
2026-10-20 13:57   ` sashiko-bot
2026-10-20 13:44 ` [PATCH v8 02/15] perf annotate-arm64: Enable instruction tracking support Tengda Wu
2026-10-20 14:05   ` sashiko-bot
2026-10-20 13:44 ` [PATCH v8 03/15] perf annotate-data: Add arch_get_reg_offset helper Tengda Wu
2026-10-20 14:13   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 04/15] perf annotate-arm64: Track return type after call instructions Tengda Wu
2026-10-20 14:15   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 05/15] perf annotate-arm64: Support load instruction tracking Tengda Wu
2026-10-20 14:36   ` sashiko-bot [this message]
2026-10-20 13:45 ` [PATCH v8 06/15] perf annotate-arm64: Support store " Tengda Wu
2026-10-20 14:39   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 07/15] perf annotate-data: Expand type_state_reg imm_value to u64 Tengda Wu
2026-10-20 14:43   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 08/15] perf annotate-data: Track imm_value for stack variables Tengda Wu
2026-10-20 14:46   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 09/15] perf annotate-x86: Delete stale stack state on store of untracked register Tengda Wu
2026-10-20 14:50   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 10/15] perf annotate-arm64: Support stack variable tracking Tengda Wu
2026-10-20 14:59   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 11/15] perf annotate-arm64: Support 'mov' instruction tracking Tengda Wu
2026-10-20 15:01   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 12/15] perf annotate-arm64: Support 'add' " Tengda Wu
2026-10-20 15:05   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 13/15] perf annotate-arm64: Support 'adrp' instruction to track global variables Tengda Wu
2026-10-20 15:15   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 14/15] perf annotate-arm64: Support per-cpu variable access tracking Tengda Wu
2026-10-20 15:24   ` sashiko-bot
2026-10-20 13:45 ` [PATCH v8 15/15] perf annotate-arm64: Support 'mrs' instruction to track 'current' pointer Tengda Wu
2026-10-20 15:30   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-179159@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=wutengda@huaweicloud.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox