summaryrefslogtreecommitdiffstats
path: root/tools/perf/arch/loongarch/annotate/instructions.c
blob: 70262d5f1444243dd95bb8e83229cf967adb5b51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// SPDX-License-Identifier: GPL-2.0
/*
 * Perf annotate functions.
 *
 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
 */

static int loongarch_call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
		struct disasm_line *dl __maybe_unused)
{
	char *c, *endptr, *tok, *name;
	struct map *map = ms->map;
	struct addr_map_symbol target = {
		.ms = { .map = map, },
	};

	c = strchr(ops->raw, '#');
	if (c++ == NULL)
		return -1;

	ops->target.addr = strtoull(c, &endptr, 16);

	name = strchr(endptr, '<');
	name++;

	if (arch->objdump.skip_functions_char &&
	    strchr(name, arch->objdump.skip_functions_char))
		return -1;

	tok = strchr(name, '>');
	if (tok == NULL)
		return -1;

	*tok = '\0';
	ops->target.name = strdup(name);
	*tok = '>';

	if (ops->target.name == NULL)
		return -1;

	target.addr = map__objdump_2mem(map, ops->target.addr);

	if (maps__find_ams(ms->maps, &target) == 0 &&
	    map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
		ops->target.sym = target.ms.sym;

	return 0;
}

static struct ins_ops loongarch_call_ops = {
	.parse	   = loongarch_call__parse,
	.scnprintf = call__scnprintf,
};

static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
		struct disasm_line *dl __maybe_unused)
{
	struct map *map = ms->map;
	struct symbol *sym = ms->sym;
	struct addr_map_symbol target = {
		.ms = { .map = map, },
	};
	const char *c = strchr(ops->raw, '#');
	u64 start, end;

	ops->jump.raw_comment = strchr(ops->raw, arch->objdump.comment_char);
	ops->jump.raw_func_start = strchr(ops->raw, '<');

	if (ops->jump.raw_func_start && c > ops->jump.raw_func_start)
		c = NULL;

	if (c++ != NULL)
		ops->target.addr = strtoull(c, NULL, 16);
	else
		ops->target.addr = strtoull(ops->raw, NULL, 16);

	target.addr = map__objdump_2mem(map, ops->target.addr);
	start = map__unmap_ip(map, sym->start);
	end = map__unmap_ip(map, sym->end);

	ops->target.outside = target.addr < start || target.addr > end;

	if (maps__find_ams(ms->maps, &target) == 0 &&
	    map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
		ops->target.sym = target.ms.sym;

	if (!ops->target.outside) {
		ops->target.offset = target.addr - start;
		ops->target.offset_avail = true;
	} else {
		ops->target.offset_avail = false;
	}

	return 0;
}

static struct ins_ops loongarch_jump_ops = {
	.parse	   = loongarch_jump__parse,
	.scnprintf = jump__scnprintf,
};

static
struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name)
{
	struct ins_ops *ops = NULL;

	if (!strcmp(name, "bl"))
		ops = &loongarch_call_ops;
	else if (!strcmp(name, "jirl"))
		ops = &ret_ops;
	else if (!strcmp(name, "b") ||
		 !strncmp(name, "beq", 3) ||
		 !strncmp(name, "bne", 3) ||
		 !strncmp(name, "blt", 3) ||
		 !strncmp(name, "bge", 3) ||
		 !strncmp(name, "bltu", 4) ||
		 !strncmp(name, "bgeu", 4))
		ops = &loongarch_jump_ops;
	else
		return NULL;

	arch__associate_ins_ops(arch, name, ops);

	return ops;
}

static
int loongarch__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
{
	if (!arch->initialized) {
		arch->associate_instruction_ops = loongarch__associate_ins_ops;
		arch->initialized = true;
		arch->objdump.comment_char = '#';
		arch->e_machine = EM_LOONGARCH;
		arch->e_flags = 0;
	}

	return 0;
}