xref: /linux/tools/perf/arch/powerpc/annotate/instructions.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
3 
powerpc__associate_instruction_ops(struct arch * arch,const char * name)4 static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, const char *name)
5 {
6 	int i;
7 	struct ins_ops *ops;
8 
9 	/*
10 	 * - Interested only if instruction starts with 'b'.
11 	 * - Few start with 'b', but aren't branch instructions.
12 	 */
13 	if (name[0] != 'b'             ||
14 	    !strncmp(name, "bcd", 3)   ||
15 	    !strncmp(name, "brinc", 5) ||
16 	    !strncmp(name, "bper", 4))
17 		return NULL;
18 
19 	ops = &jump_ops;
20 
21 	i = strlen(name) - 1;
22 	if (i < 0)
23 		return NULL;
24 
25 	/* ignore optional hints at the end of the instructions */
26 	if (name[i] == '+' || name[i] == '-')
27 		i--;
28 
29 	if (name[i] == 'l' || (name[i] == 'a' && name[i-1] == 'l')) {
30 		/*
31 		 * if the instruction ends up with 'l' or 'la', then
32 		 * those are considered 'calls' since they update LR.
33 		 * ... except for 'bnl' which is branch if not less than
34 		 * and the absolute form of the same.
35 		 */
36 		if (strcmp(name, "bnl") && strcmp(name, "bnl+") &&
37 		    strcmp(name, "bnl-") && strcmp(name, "bnla") &&
38 		    strcmp(name, "bnla+") && strcmp(name, "bnla-"))
39 			ops = &call_ops;
40 	}
41 	if (name[i] == 'r' && name[i-1] == 'l')
42 		/*
43 		 * instructions ending with 'lr' are considered to be
44 		 * return instructions
45 		 */
46 		ops = &ret_ops;
47 
48 	arch__associate_ins_ops(arch, name, ops);
49 	return ops;
50 }
51 
52 #define PPC_OP(op)	(((op) >> 26) & 0x3F)
53 #define PPC_21_30(R)	(((R) >> 1) & 0x3ff)
54 #define PPC_22_30(R)	(((R) >> 1) & 0x1ff)
55 
56 struct insn_offset {
57 	const char	*name;
58 	int		value;
59 };
60 
61 /*
62  * There are memory instructions with opcode 31 which are
63  * of X Form, Example:
64  * ldx RT,RA,RB
65  * ______________________________________
66  * | 31 |  RT  |  RA |  RB |   21     |/|
67  * --------------------------------------
68  * 0    6     11    16    21         30 31
69  *
70  * But all instructions with opcode 31 are not memory.
71  * Example: add RT,RA,RB
72  *
73  * Use bits 21 to 30 to check memory insns with 31 as opcode.
74  * In ins_array below, for ldx instruction:
75  * name => OP_31_XOP_LDX
76  * value => 21
77  */
78 
79 static struct insn_offset ins_array[] = {
80 	{ .name = "OP_31_XOP_LXSIWZX",  .value = 12, },
81 	{ .name = "OP_31_XOP_LWARX",	.value = 20, },
82 	{ .name = "OP_31_XOP_LDX",	.value = 21, },
83 	{ .name = "OP_31_XOP_LWZX",	.value = 23, },
84 	{ .name = "OP_31_XOP_LDUX",	.value = 53, },
85 	{ .name = "OP_31_XOP_LWZUX",	.value = 55, },
86 	{ .name = "OP_31_XOP_LXSIWAX",  .value = 76, },
87 	{ .name = "OP_31_XOP_LDARX",    .value = 84, },
88 	{ .name = "OP_31_XOP_LBZX",	.value = 87, },
89 	{ .name = "OP_31_XOP_LVX",      .value = 103, },
90 	{ .name = "OP_31_XOP_LBZUX",    .value = 119, },
91 	{ .name = "OP_31_XOP_STXSIWX",  .value = 140, },
92 	{ .name = "OP_31_XOP_STDX",	.value = 149, },
93 	{ .name = "OP_31_XOP_STWX",	.value = 151, },
94 	{ .name = "OP_31_XOP_STDUX",	.value = 181, },
95 	{ .name = "OP_31_XOP_STWUX",	.value = 183, },
96 	{ .name = "OP_31_XOP_STBX",	.value = 215, },
97 	{ .name = "OP_31_XOP_STVX",     .value = 231, },
98 	{ .name = "OP_31_XOP_STBUX",	.value = 247, },
99 	{ .name = "OP_31_XOP_LHZX",	.value = 279, },
100 	{ .name = "OP_31_XOP_LHZUX",	.value = 311, },
101 	{ .name = "OP_31_XOP_LXVDSX",   .value = 332, },
102 	{ .name = "OP_31_XOP_LWAX",	.value = 341, },
103 	{ .name = "OP_31_XOP_LHAX",	.value = 343, },
104 	{ .name = "OP_31_XOP_LWAUX",	.value = 373, },
105 	{ .name = "OP_31_XOP_LHAUX",	.value = 375, },
106 	{ .name = "OP_31_XOP_STHX",	.value = 407, },
107 	{ .name = "OP_31_XOP_STHUX",	.value = 439, },
108 	{ .name = "OP_31_XOP_LXSSPX",   .value = 524, },
109 	{ .name = "OP_31_XOP_LDBRX",	.value = 532, },
110 	{ .name = "OP_31_XOP_LSWX",	.value = 533, },
111 	{ .name = "OP_31_XOP_LWBRX",	.value = 534, },
112 	{ .name = "OP_31_XOP_LFSUX",    .value = 567, },
113 	{ .name = "OP_31_XOP_LXSDX",    .value = 588, },
114 	{ .name = "OP_31_XOP_LSWI",	.value = 597, },
115 	{ .name = "OP_31_XOP_LFDX",     .value = 599, },
116 	{ .name = "OP_31_XOP_LFDUX",    .value = 631, },
117 	{ .name = "OP_31_XOP_STXSSPX",  .value = 652, },
118 	{ .name = "OP_31_XOP_STDBRX",	.value = 660, },
119 	{ .name = "OP_31_XOP_STXWX",	.value = 661, },
120 	{ .name = "OP_31_XOP_STWBRX",	.value = 662, },
121 	{ .name = "OP_31_XOP_STFSX",	.value = 663, },
122 	{ .name = "OP_31_XOP_STFSUX",	.value = 695, },
123 	{ .name = "OP_31_XOP_STXSDX",   .value = 716, },
124 	{ .name = "OP_31_XOP_STSWI",	.value = 725, },
125 	{ .name = "OP_31_XOP_STFDX",	.value = 727, },
126 	{ .name = "OP_31_XOP_STFDUX",	.value = 759, },
127 	{ .name = "OP_31_XOP_LXVW4X",   .value = 780, },
128 	{ .name = "OP_31_XOP_LHBRX",	.value = 790, },
129 	{ .name = "OP_31_XOP_LXVD2X",   .value = 844, },
130 	{ .name = "OP_31_XOP_LFIWAX",	.value = 855, },
131 	{ .name = "OP_31_XOP_LFIWZX",	.value = 887, },
132 	{ .name = "OP_31_XOP_STXVW4X",  .value = 908, },
133 	{ .name = "OP_31_XOP_STHBRX",	.value = 918, },
134 	{ .name = "OP_31_XOP_STXVD2X",  .value = 972, },
135 	{ .name = "OP_31_XOP_STFIWX",	.value = 983, },
136 };
137 
138 /*
139  * Arithmetic instructions which are having opcode as 31.
140  * These instructions are tracked to save the register state
141  * changes. Example:
142  *
143  * lwz	r10,264(r3)
144  * add	r31, r3, r3
145  * lwz	r9, 0(r31)
146  *
147  * Here instruction tracking needs to identify the "add"
148  * instruction and save data type of r3 to r31. If a sample
149  * is hit at next "lwz r9, 0(r31)", by this instruction tracking,
150  * data type of r31 can be resolved.
151  */
152 static struct insn_offset arithmetic_ins_op_31[] = {
153 	{ .name = "SUB_CARRY_XO_FORM",  .value = 8, },
154 	{ .name = "MUL_HDW_XO_FORM1",   .value = 9, },
155 	{ .name = "ADD_CARRY_XO_FORM",  .value = 10, },
156 	{ .name = "MUL_HW_XO_FORM1",    .value = 11, },
157 	{ .name = "SUB_XO_FORM",        .value = 40, },
158 	{ .name = "MUL_HDW_XO_FORM",    .value = 73, },
159 	{ .name = "MUL_HW_XO_FORM",     .value = 75, },
160 	{ .name = "SUB_EXT_XO_FORM",    .value = 136, },
161 	{ .name = "ADD_EXT_XO_FORM",    .value = 138, },
162 	{ .name = "SUB_ZERO_EXT_XO_FORM",       .value = 200, },
163 	{ .name = "ADD_ZERO_EXT_XO_FORM",       .value = 202, },
164 	{ .name = "SUB_EXT_XO_FORM2",   .value = 232, },
165 	{ .name = "MUL_DW_XO_FORM",     .value = 233, },
166 	{ .name = "ADD_EXT_XO_FORM2",   .value = 234, },
167 	{ .name = "MUL_W_XO_FORM",      .value = 235, },
168 	{ .name = "ADD_XO_FORM",	.value = 266, },
169 	{ .name = "DIV_DW_XO_FORM1",    .value = 457, },
170 	{ .name = "DIV_W_XO_FORM1",     .value = 459, },
171 	{ .name = "DIV_DW_XO_FORM",	.value = 489, },
172 	{ .name = "DIV_W_XO_FORM",	.value = 491, },
173 };
174 
175 static struct insn_offset arithmetic_two_ops[] = {
176 	{ .name = "mulli",      .value = 7, },
177 	{ .name = "subfic",     .value = 8, },
178 	{ .name = "addic",      .value = 12, },
179 	{ .name = "addic.",     .value = 13, },
180 	{ .name = "addi",       .value = 14, },
181 	{ .name = "addis",      .value = 15, },
182 };
183 
cmp_offset(const void * a,const void * b)184 static int cmp_offset(const void *a, const void *b)
185 {
186 	const struct insn_offset *val1 = a;
187 	const struct insn_offset *val2 = b;
188 
189 	return (val1->value - val2->value);
190 }
191 
check_ppc_insn(struct disasm_line * dl)192 static struct ins_ops *check_ppc_insn(struct disasm_line *dl)
193 {
194 	int raw_insn = dl->raw.raw_insn;
195 	int opcode = PPC_OP(raw_insn);
196 	int mem_insn_31 = PPC_21_30(raw_insn);
197 	struct insn_offset *ret;
198 	struct insn_offset mem_insns_31_opcode = {
199 		"OP_31_INSN",
200 		mem_insn_31
201 	};
202 	char name_insn[32];
203 
204 	/*
205 	 * Instructions with opcode 32 to 63 are memory
206 	 * instructions in powerpc
207 	 */
208 	if ((opcode & 0x20)) {
209 		/*
210 		 * Set name in case of raw instruction to
211 		 * opcode to be used in insn-stat
212 		 */
213 		if (!strlen(dl->ins.name)) {
214 			sprintf(name_insn, "%d", opcode);
215 			dl->ins.name = strdup(name_insn);
216 		}
217 		return &load_store_ops;
218 	} else if (opcode == 31) {
219 		/* Check for memory instructions with opcode 31 */
220 		ret = bsearch(&mem_insns_31_opcode, ins_array, ARRAY_SIZE(ins_array), sizeof(ins_array[0]), cmp_offset);
221 		if (ret) {
222 			if (!strlen(dl->ins.name))
223 				dl->ins.name = strdup(ret->name);
224 			return &load_store_ops;
225 		} else {
226 			mem_insns_31_opcode.value = PPC_22_30(raw_insn);
227 			ret = bsearch(&mem_insns_31_opcode, arithmetic_ins_op_31, ARRAY_SIZE(arithmetic_ins_op_31),
228 					sizeof(arithmetic_ins_op_31[0]), cmp_offset);
229 			if (ret != NULL)
230 				return &arithmetic_ops;
231 			/* Bits 21 to 30 has value 444 for "mr" insn ie, OR X form */
232 			if (PPC_21_30(raw_insn) == 444)
233 				return &arithmetic_ops;
234 		}
235 	} else {
236 		mem_insns_31_opcode.value = opcode;
237 		ret = bsearch(&mem_insns_31_opcode, arithmetic_two_ops, ARRAY_SIZE(arithmetic_two_ops),
238 				sizeof(arithmetic_two_ops[0]), cmp_offset);
239 		if (ret != NULL)
240 			return &arithmetic_ops;
241 	}
242 
243 	return NULL;
244 }
245 
246 /*
247  * Instruction tracking function to track register state moves.
248  * Example sequence:
249  *    ld      r10,264(r3)
250  *    mr      r31,r3
251  *    <<after some sequence>
252  *    ld      r9,312(r31)
253  *
254  * Previous instruction sequence shows that register state of r3
255  * is moved to r31. update_insn_state_powerpc tracks these state
256  * changes
257  */
258 #ifdef HAVE_DWARF_SUPPORT
update_insn_state_powerpc(struct type_state * state,struct data_loc_info * dloc,Dwarf_Die * cu_die __maybe_unused,struct disasm_line * dl)259 static void update_insn_state_powerpc(struct type_state *state,
260 		struct data_loc_info *dloc, Dwarf_Die * cu_die __maybe_unused,
261 		struct disasm_line *dl)
262 {
263 	struct annotated_insn_loc loc;
264 	struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];
265 	struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];
266 	struct type_state_reg *tsr;
267 	u32 insn_offset = dl->al.offset;
268 
269 	if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)
270 		return;
271 
272 	/*
273 	 * Value 444 for bits 21:30 is for "mr"
274 	 * instruction. "mr" is extended OR. So set the
275 	 * source and destination reg correctly
276 	 */
277 	if (PPC_21_30(dl->raw.raw_insn) == 444) {
278 		int src_reg = src->reg1;
279 
280 		src->reg1 = dst->reg1;
281 		dst->reg1 = src_reg;
282 	}
283 
284 	if (!has_reg_type(state, dst->reg1))
285 		return;
286 
287 	tsr = &state->regs[dst->reg1];
288 
289 	if (!has_reg_type(state, src->reg1) ||
290 			!state->regs[src->reg1].ok) {
291 		tsr->ok = false;
292 		return;
293 	}
294 
295 	tsr->type = state->regs[src->reg1].type;
296 	tsr->kind = state->regs[src->reg1].kind;
297 	tsr->ok = true;
298 
299 	pr_debug_dtp("mov [%x] reg%d -> reg%d",
300 			insn_offset, src->reg1, dst->reg1);
301 	pr_debug_type_name(&tsr->type, tsr->kind);
302 }
303 #endif /* HAVE_DWARF_SUPPORT */
304 
powerpc__annotate_init(struct arch * arch,char * cpuid __maybe_unused)305 static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
306 {
307 	if (!arch->initialized) {
308 		arch->initialized = true;
309 		arch->associate_instruction_ops = powerpc__associate_instruction_ops;
310 		arch->objdump.comment_char      = '#';
311 		annotate_opts.show_asm_raw = true;
312 	}
313 
314 	return 0;
315 }
316