perf annotate: add Alpha instruction support

Teach perf annotate about the Alpha control-transfer instructions so jump/call/
return arrows and cross-CPU annotation work for Alpha perf.data.

Add tools/perf/util/annotate-arch/annotate-alpha.c with arch__new_alpha() and an
associate_instruction_ops() that classifies:
  call:   bsr, jsr, jsr_coroutine
  ret:    ret
  jump:   br, jmp, and the conditional branches beq/bne/blt/ble/bgt/bge/blbc/
          blbs and fbeq/fbne/fblt/fble/fbgt/fbge
  mov:    mov, fmov (objdump pseudos)

EM_ALPHA is 0x9026, far too large to index the e_machine-keyed arch_new_fn[]
table in arch__find(), so select arch__new_alpha explicitly before the table
lookup. Declare it in disasm.h and add the object to the annotate-arch Build.

Disassembly itself comes from objdump/binutils, which already supports Alpha;
this provides perf's instruction-class metadata for annotation.
diff --git a/tools/perf/util/annotate-arch/Build b/tools/perf/util/annotate-arch/Build
index 23316743..0a74e1a 100644
--- a/tools/perf/util/annotate-arch/Build
+++ b/tools/perf/util/annotate-arch/Build
@@ -1,3 +1,4 @@
+perf-util-y += annotate-alpha.o
 perf-util-y += annotate-arc.o
 perf-util-y += annotate-arm.o
 perf-util-y += annotate-arm64.o
diff --git a/tools/perf/util/annotate-arch/annotate-alpha.c b/tools/perf/util/annotate-arch/annotate-alpha.c
new file mode 100644
index 0000000..7ef20a4
--- /dev/null
+++ b/tools/perf/util/annotate-arch/annotate-alpha.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <string.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include "../../util/disasm.h"
+
+/*
+ * Alpha control-transfer instructions, as printed by objdump:
+ *
+ *   PC-relative (opcode group 0x30-0x3f):
+ *     br, bsr                          unconditional / to-subroutine
+ *     beq bne blt ble bgt bge blbc blbs   integer conditional
+ *     fbeq fbne fblt fble fbgt fbge       floating conditional
+ *
+ *   Register-indirect (JSR group, opcode 0x1a):
+ *     jmp, jsr, ret, jsr_coroutine
+ *
+ * bsr/jsr (and the coroutine form) save a return address, so they are calls;
+ * ret returns; everything else that transfers control is a jump. Alpha has no
+ * machine "mov" -- objdump prints "mov"/"fmov" as pseudos for bis/cpys, so map
+ * them to mov_ops when present.
+ */
+
+static int is_alpha_cond_branch(const char *name)
+{
+	static const char *const branches[] = {
+		"beq", "bne", "blt", "ble", "bgt", "bge", "blbc", "blbs",
+		"fbeq", "fbne", "fblt", "fble", "fbgt", "fbge",
+	};
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(branches); i++) {
+		if (!strcmp(name, branches[i]))
+			return 1;
+	}
+	return 0;
+}
+
+static const struct ins_ops *alpha__associate_instruction_ops(struct arch *arch, const char *name)
+{
+	const struct ins_ops *ops = NULL;
+
+	if (!strcmp(name, "bsr") ||
+	    !strcmp(name, "jsr") ||
+	    !strcmp(name, "jsr_coroutine")) {
+		ops = &call_ops;
+	} else if (!strcmp(name, "ret")) {
+		ops = &ret_ops;
+	} else if (!strcmp(name, "br") ||
+		   !strcmp(name, "jmp") ||
+		   is_alpha_cond_branch(name)) {
+		ops = &jump_ops;
+	} else if (!strcmp(name, "mov") ||
+		   !strcmp(name, "fmov")) {
+		ops = &mov_ops;
+	}
+
+	if (ops)
+		arch__associate_ins_ops(arch, name, ops);
+
+	return ops;
+}
+
+const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id,
+				   const char *cpuid __maybe_unused)
+{
+	struct arch *arch = zalloc(sizeof(*arch));
+
+	if (!arch)
+		return NULL;
+
+	arch->name = "alpha";
+	arch->id = *id;
+	arch->associate_instruction_ops = alpha__associate_instruction_ops;
+	arch->objdump.comment_char = '#';
+	return arch;
+}
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 59ba88e..69119b1 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -161,6 +161,8 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
 		.e_flags = e_flags,
 	};
 	const struct arch *result = NULL, **tmp;
+	const struct arch *(*new_fn)(const struct e_machine_and_e_flags *id,
+				     const char *cpuid) = NULL;
 
 	if (num_archs > 0) {
 		tmp = bsearch(&key, archs, num_archs, sizeof(*archs), arch__key_cmp);
@@ -171,7 +173,16 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
 	if (result)
 		return result;
 
-	if (e_machine >= ARRAY_SIZE(arch_new_fn) || arch_new_fn[e_machine] == NULL) {
+	/*
+	 * EM_ALPHA (0x9026) is far too large to index arch_new_fn[], so it is
+	 * selected explicitly; everything else uses the e_machine-indexed table.
+	 */
+	if (e_machine == EM_ALPHA)
+		new_fn = arch__new_alpha;
+	else if (e_machine < ARRAY_SIZE(arch_new_fn))
+		new_fn = arch_new_fn[e_machine];
+
+	if (new_fn == NULL) {
 		errno = ENOTSUP;
 		return NULL;
 	}
@@ -180,7 +191,7 @@ const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *
 	if (!tmp)
 		return NULL;
 
-	result = arch_new_fn[e_machine](&key, cpuid);
+	result = new_fn(&key, cpuid);
 	if (!result) {
 		pr_err("%s: failed to initialize %s (%u) arch priv area\n",
 			__func__, result->name, e_machine);
diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h
index 25756e3..adbdbf8 100644
--- a/tools/perf/util/disasm.h
+++ b/tools/perf/util/disasm.h
@@ -122,6 +122,7 @@ extern const struct ins_ops ret_ops;
 
 int arch__associate_ins_ops(struct arch *arch, const char *name, const struct ins_ops *ops);
 
+const struct arch *arch__new_alpha(const struct e_machine_and_e_flags *id, const char *cpuid);
 const struct arch *arch__new_arc(const struct e_machine_and_e_flags *id, const char *cpuid);
 const struct arch *arch__new_arm(const struct e_machine_and_e_flags *id, const char *cpuid);
 const struct arch *arch__new_arm64(const struct e_machine_and_e_flags *id, const char *cpuid);