[PATCH v5 09/26] perf annotate-arm64: Implement extract_op_location() callback
From: Tengda Wu
Date: Tue Sep 08 2026 - 09:15:31 EST
Implement the extract_op_location() callback for the arm64 architecture
to handle its specific assembly syntax and addressing modes.
The extractor handles:
1. Immediate operands (e.g., '#0x10' or '#0xc600, lsl #16').
2. Memory references with diverse addressing modes:
- Signed offset: [base{, #imm|reg}]
- Pre-index: [base, #imm]!
- Post-index: [base], #imm|reg
3. Multi-register operands and primary/secondary register extraction.
4. Register extension specifiers (e.g., uxtw, sxtx) and shift amounts
for shifted/extended operands.
With this callback in place, 'perf annotate' can now resolve memory
locations and register types on arm64, marking the initial enablement
of data type profiling for this architecture.
Signed-off-by: Tengda Wu <wutengda@xxxxxxxxxxxxxxx>
---
.../perf/util/annotate-arch/annotate-arm64.c | 127 ++++++++++++++++++
tools/perf/util/annotate.h | 61 +++++++++
2 files changed, 188 insertions(+)
diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c
index a7595a954cfa..d8a5904359b1 100644
--- a/tools/perf/util/annotate-arch/annotate-arm64.c
+++ b/tools/perf/util/annotate-arch/annotate-arm64.c
@@ -316,6 +316,132 @@ static const struct ins_ops *arm64__associate_instruction_ops(struct arch *arch,
return ops;
}
+static enum annotated_ext_type arm64__check_ext_type(const char *op)
+{
+ if (!strncmp(op, "uxtb", 4)) return PERF_EXT_UXTB;
+ if (!strncmp(op, "uxth", 4)) return PERF_EXT_UXTH;
+ if (!strncmp(op, "uxtw", 4)) return PERF_EXT_UXTW;
+ if (!strncmp(op, "uxtx", 4)) return PERF_EXT_UXTX;
+ if (!strncmp(op, "sxtb", 4)) return PERF_EXT_SXTB;
+ if (!strncmp(op, "sxth", 4)) return PERF_EXT_SXTH;
+ if (!strncmp(op, "sxtw", 4)) return PERF_EXT_SXTW;
+ if (!strncmp(op, "sxtx", 4)) return PERF_EXT_SXTX;
+
+ return PERF_EXT_NONE;
+}
+
+static enum annotated_shift_type arm64__check_shift_type(const char *op)
+{
+ if (!strncmp(op, "lsl", 3)) return PERF_SHIFT_LSL;
+ if (!strncmp(op, "lsr", 3)) return PERF_SHIFT_LSR;
+ if (!strncmp(op, "asr", 3)) return PERF_SHIFT_ASR;
+ if (!strncmp(op, "ror", 3)) return PERF_SHIFT_ROR;
+
+ return PERF_SHIFT_NONE;
+}
+
+static void extract_op_location_arm64(const struct arch *arch,
+ struct disasm_line *dl __maybe_unused,
+ const char *op_str, int op_idx __maybe_unused,
+ struct annotated_op_loc *op_loc)
+{
+ const char *s = op_str;
+ char *p = NULL;
+
+ if (op_str == NULL)
+ return;
+
+ /*
+ * Handle immediate operand.
+ * e.g., "#0xc600" -> offset = 0xc600, imm = true
+ *
+ * After parsing, check for post-modifiers like "lsl #16", which
+ * typically occur in instructions such as movk, movz, movn, etc.
+ */
+ if (*s == arch->objdump.imm_char) {
+ op_loc->offset = strtol(s + 1, &p, 0);
+ if (p && p != s + 1) {
+ op_loc->imm = true;
+ s = p;
+ }
+ goto check_modifiers;
+ }
+
+ /*
+ * Handle memory references, identify arm64 specific addressing modes.
+ * Reference: Arm Architecture Reference Manual
+ * (DDI 0487), Chapter C1.3.3: Load/store addressing modes.
+ */
+ if (*s == arch->objdump.memory_ref_char) {
+ op_loc->mem_ref = true;
+
+ p = strchr(s, ']');
+ if (p == NULL)
+ return;
+
+ /* Pre-index: [base, #imm]! */
+ if (p[1] == '!')
+ op_loc->addr_mode = PERF_AAM_PRE_INDEX;
+ /* Post-index: [base], #imm|reg */
+ else if (p[1] == ',' &&
+ (strchr(p + 1, arch->objdump.imm_char) ||
+ arm64__is_reg(skip_spaces(p + 2))))
+ op_loc->addr_mode = PERF_AAM_POST_INDEX;
+ /* Signed offset: [base{, #imm|reg}] */
+ else
+ op_loc->addr_mode = PERF_AAM_SIGNED_OFFSET;
+
+ s++;
+ }
+
+ /* Extract the primary register */
+ op_loc->reg1 = arch__dwarf_regnum(arch, s);
+ if (op_loc->reg1 == -1)
+ return;
+
+ /* Move to the next operand field, if present */
+ s = strchr(s, ',');
+ if (s == NULL)
+ return;
+ s = skip_spaces(s + 1);
+
+ /* Extract secondary register or immediate offset */
+ if (op_loc->multi_regs)
+ op_loc->reg2 = arch__dwarf_regnum(arch, s);
+ else if (*s == arch->objdump.imm_char)
+ op_loc->offset = strtol(s + 1, NULL, 0);
+
+check_modifiers:
+ /*
+ * Look for a following shift or extension modifier:
+ * ", lsl #3" -> extend_type = PERF_EXT_NONE,
+ * shift_type = PERF_SHIFT_LSL, amount = 3
+ * ", uxtw #3" -> extend_type = PERF_EXT_UXTW,
+ * shift_type = PERF_SHIFT_LSL, amount = 3
+ * ", uxtw" -> extend_type = PERF_EXT_UXTW,
+ * shift_type = PERF_SHIFT_LSL, amount = 0
+ */
+ s = strchr(s, ',');
+ if (s == NULL)
+ return;
+ s = skip_spaces(s + 1);
+
+ op_loc->extend_type = arm64__check_ext_type(s);
+ op_loc->shift_type = arm64__check_shift_type(s);
+ /* ARM64 extended operands are implicitly shifted by LSL. */
+ if (op_loc->extend_type != PERF_EXT_NONE)
+ op_loc->shift_type = PERF_SHIFT_LSL;
+
+ /* Parse shift amount if present */
+ op_loc->amount = 0;
+ if (op_loc->extend_type != PERF_EXT_NONE ||
+ op_loc->shift_type != PERF_SHIFT_NONE) {
+ s = strchr(s, arch->objdump.imm_char);
+ if (s)
+ op_loc->amount = (u8)strtol(s + 1, NULL, 0);
+ }
+}
+
const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id,
const char *cpuid __maybe_unused)
{
@@ -334,6 +460,7 @@ const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id,
arch->objdump.memory_ref_char = '[';
arch->objdump.imm_char = '#';
arch->associate_instruction_ops = arm64__associate_instruction_ops;
+ arch->extract_op_location = extract_op_location_arm64;
/* bl, blr */
err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED);
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 11b6e4780c02..f0ff4b7e8a28 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -500,6 +500,10 @@ int arch__dwarf_regnum(const struct arch *arch, const char *str);
* @reg2: Second register in the operand
* @offset: Memory access offset in the operand
* @segment: Segment selector register
+ * @addr_mode: Addressing mode, only valid if @mem_ref is true
+ * @extend_type: Operand extension type (enum annotated_ext_type)
+ * @shift_type: Operand shift type (enum annotated_shift_type)
+ * @amount: Number of bits to shift
* @mem_ref: Whether the operand accesses memory
* @multi_regs: Whether the second register is used
* @imm: Whether the operand is an immediate value (in offset)
@@ -509,6 +513,10 @@ struct annotated_op_loc {
int reg2;
int offset;
u8 segment;
+ u8 addr_mode;
+ u8 extend_type;
+ u8 shift_type;
+ u8 amount;
bool mem_ref;
bool multi_regs;
bool imm;
@@ -532,6 +540,59 @@ enum annotated_x86_segment {
INSN_SEG_X86_SS,
};
+/*
+ * ARM64 addressing modes for memory operations.
+ *
+ * [Xn, #imm] -> SIGNED_OFFSET (base + offset, base unchanged)
+ * [Xn, #imm]! -> PRE_INDEX (base += offset, then access)
+ * [Xn], #imm -> POST_INDEX (access, then base += offset)
+ */
+enum annotated_addr_mode {
+ PERF_AAM_NONE = 0,
+
+ PERF_AAM_SIGNED_OFFSET,
+ PERF_AAM_PRE_INDEX,
+ PERF_AAM_POST_INDEX,
+};
+
+/*
+ * ARM64 register extension types.
+ * UXT* = zero-extend, SXT* = sign-extend.
+ * B=8bit, H=16bit, W=32bit, X=64bit.
+ *
+ * Example: UXTW = zero-extend 32-bit Wn to 64-bit Xn
+ */
+enum annotated_ext_type {
+ PERF_EXT_NONE = 0,
+
+ PERF_EXT_UXTB,
+ PERF_EXT_UXTH,
+ PERF_EXT_UXTW,
+ PERF_EXT_UXTX,
+ PERF_EXT_SXTB,
+ PERF_EXT_SXTH,
+ PERF_EXT_SXTW,
+ PERF_EXT_SXTX,
+};
+
+/*
+ * ARM64 operand shift types.
+ * LSL = logical shift left.
+ * LSR = logical shift right.
+ * ASR = arithmetic shift right.
+ * ROR = rotate right.
+ *
+ * Example: LSL #3 = shift the operand left by 3 bits.
+ */
+enum annotated_shift_type {
+ PERF_SHIFT_NONE = 0,
+
+ PERF_SHIFT_LSL,
+ PERF_SHIFT_LSR,
+ PERF_SHIFT_ASR,
+ PERF_SHIFT_ROR,
+};
+
/**
* struct annotated_insn_loc - Location info of instruction
* @ops: Array of location info for source and target operands
--
2.34.1