1 | /* Generate code from machine description to extract operands from insn as rtl. |
2 | Copyright (C) 1987-2024 Free Software Foundation, Inc. |
3 | |
4 | This file is part of GCC. |
5 | |
6 | GCC is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free |
8 | Software Foundation; either version 3, or (at your option) any later |
9 | version. |
10 | |
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with GCC; see the file COPYING3. If not see |
18 | <http://www.gnu.org/licenses/>. */ |
19 | |
20 | |
21 | #include "bconfig.h" |
22 | #include "system.h" |
23 | #include "coretypes.h" |
24 | #include "tm.h" |
25 | #include "rtl.h" |
26 | #include "errors.h" |
27 | #include "read-md.h" |
28 | #include "gensupport.h" |
29 | |
30 | /* This structure contains all the information needed to describe one |
31 | set of extractions methods. Each method may be used by more than |
32 | one pattern if the operands are in the same place. |
33 | |
34 | The string for each operand describes that path to the operand and |
35 | contains `0' through `9' when going into an expression and `a' through |
36 | `z' then 'A' through to 'Z' when going into a vector. We assume here that |
37 | only the first operand of an rtl expression is a vector. genrecog.cc makes |
38 | the same assumption (and uses the same representation) and it is currently |
39 | true. */ |
40 | |
41 | typedef char *locstr; |
42 | |
43 | struct |
44 | { |
45 | unsigned int ; |
46 | unsigned int ; |
47 | locstr *; |
48 | locstr *; |
49 | int *; |
50 | struct code_ptr *; |
51 | struct extraction *; |
52 | }; |
53 | |
54 | /* Holds a single insn code that uses an extraction method. */ |
55 | struct code_ptr |
56 | { |
57 | int insn_code; |
58 | struct code_ptr *next; |
59 | }; |
60 | |
61 | /* All extractions needed for this machine description. */ |
62 | static struct extraction *; |
63 | |
64 | /* All insn codes for old-style peepholes. */ |
65 | static struct code_ptr *peepholes; |
66 | |
67 | /* This structure is used by gen_insn and walk_rtx to accumulate the |
68 | data that will be used to produce an extractions structure. */ |
69 | |
70 | |
71 | class |
72 | { |
73 | public: |
74 | () : oplocs (10), duplocs (10), dupnums (10), pathstr (20) {} |
75 | |
76 | auto_vec<locstr> ; |
77 | auto_vec<locstr> ; |
78 | auto_vec<int> ; |
79 | auto_vec<char> ; |
80 | }; |
81 | |
82 | /* Forward declarations. */ |
83 | static void walk_rtx (md_rtx_info *, rtx, class accum_extract *); |
84 | |
85 | #define UPPER_OFFSET ('A' - ('z' - 'a' + 1)) |
86 | |
87 | /* Convert integer OPERAND into a character - either into [a-zA-Z] for vector |
88 | operands or [0-9] for integer operands - and push onto the end of the path |
89 | in ACC. */ |
90 | static void |
91 | push_pathstr_operand (int operand, bool is_vector, |
92 | class accum_extract *acc) |
93 | { |
94 | if (is_vector && 'a' + operand > 'z') |
95 | acc->pathstr.safe_push (obj: operand + UPPER_OFFSET); |
96 | else if (is_vector) |
97 | acc->pathstr.safe_push (obj: operand + 'a'); |
98 | else |
99 | acc->pathstr.safe_push (obj: operand + '0'); |
100 | } |
101 | |
102 | static void |
103 | gen_insn (md_rtx_info *info) |
104 | { |
105 | int i; |
106 | unsigned int op_count, dup_count, j; |
107 | struct extraction *p; |
108 | struct code_ptr *link; |
109 | class accum_extract acc; |
110 | |
111 | /* Walk the insn's pattern, remembering at all times the path |
112 | down to the walking point. */ |
113 | |
114 | rtx insn = info->def; |
115 | if (XVECLEN (insn, 1) == 1) |
116 | walk_rtx (info, XVECEXP (insn, 1, 0), &acc); |
117 | else |
118 | for (i = XVECLEN (insn, 1) - 1; i >= 0; i--) |
119 | { |
120 | push_pathstr_operand (operand: i, is_vector: true, acc: &acc); |
121 | walk_rtx (info, XVECEXP (insn, 1, i), &acc); |
122 | acc.pathstr.pop (); |
123 | } |
124 | |
125 | link = XNEW (struct code_ptr); |
126 | link->insn_code = info->index; |
127 | |
128 | /* See if we find something that already had this extraction method. */ |
129 | |
130 | op_count = acc.oplocs.length (); |
131 | dup_count = acc.duplocs.length (); |
132 | gcc_assert (dup_count == acc.dupnums.length ()); |
133 | |
134 | for (p = extractions; p; p = p->next) |
135 | { |
136 | if (p->op_count != op_count || p->dup_count != dup_count) |
137 | continue; |
138 | |
139 | for (j = 0; j < op_count; j++) |
140 | { |
141 | char *a = p->oplocs[j]; |
142 | char *b = acc.oplocs[j]; |
143 | if (a != b && (!a || !b || strcmp (s1: a, s2: b))) |
144 | break; |
145 | } |
146 | |
147 | if (j != op_count) |
148 | continue; |
149 | |
150 | for (j = 0; j < dup_count; j++) |
151 | if (p->dupnums[j] != acc.dupnums[j] |
152 | || strcmp (s1: p->duplocs[j], s2: acc.duplocs[j])) |
153 | break; |
154 | |
155 | if (j != dup_count) |
156 | continue; |
157 | |
158 | /* This extraction is the same as ours. Just link us in. */ |
159 | link->next = p->insns; |
160 | p->insns = link; |
161 | return; |
162 | } |
163 | |
164 | /* Otherwise, make a new extraction method. We stash the arrays |
165 | after the extraction structure in memory. */ |
166 | |
167 | p = XNEWVAR (struct extraction, sizeof (struct extraction) |
168 | + op_count*sizeof (char *) |
169 | + dup_count*sizeof (char *) |
170 | + dup_count*sizeof (int)); |
171 | p->op_count = op_count; |
172 | p->dup_count = dup_count; |
173 | p->next = extractions; |
174 | extractions = p; |
175 | p->insns = link; |
176 | link->next = 0; |
177 | |
178 | p->oplocs = (char **)((char *)p + sizeof (struct extraction)); |
179 | p->duplocs = p->oplocs + op_count; |
180 | p->dupnums = (int *)(p->duplocs + dup_count); |
181 | |
182 | memcpy (dest: p->oplocs, src: acc.oplocs.address (), n: op_count * sizeof (locstr)); |
183 | memcpy (dest: p->duplocs, src: acc.duplocs.address (), n: dup_count * sizeof (locstr)); |
184 | memcpy (dest: p->dupnums, src: acc.dupnums.address (), n: dup_count * sizeof (int)); |
185 | } |
186 | |
187 | /* Helper subroutine of walk_rtx: given a vec<locstr>, an index, and a |
188 | string, insert the string at the index, which should either already |
189 | exist and be NULL, or not yet exist within the vector. In the latter |
190 | case the vector is enlarged as appropriate. INFO describes the |
191 | containing define_* expression. */ |
192 | static void |
193 | VEC_safe_set_locstr (md_rtx_info *info, vec<locstr> *vp, |
194 | unsigned int ix, char *str) |
195 | { |
196 | if (ix < (*vp).length ()) |
197 | { |
198 | if ((*vp)[ix]) |
199 | { |
200 | message_at (info->loc, "repeated operand number %d" , ix); |
201 | have_error = 1; |
202 | } |
203 | else |
204 | (*vp)[ix] = str; |
205 | } |
206 | else |
207 | { |
208 | while (ix > (*vp).length ()) |
209 | vp->safe_push (NULL); |
210 | vp->safe_push (obj: str); |
211 | } |
212 | } |
213 | |
214 | /* Another helper subroutine of walk_rtx: given a vec<char>, convert it |
215 | to a NUL-terminated string in malloc memory. */ |
216 | static char * |
217 | VEC_char_to_string (const vec<char> &v) |
218 | { |
219 | size_t n = v.length (); |
220 | char *s = XNEWVEC (char, n + 1); |
221 | memcpy (dest: s, src: v.address (), n: n); |
222 | s[n] = '\0'; |
223 | return s; |
224 | } |
225 | |
226 | static void |
227 | (md_rtx_info *info, rtx x, class accum_extract *acc) |
228 | { |
229 | RTX_CODE code; |
230 | int i, len; |
231 | const char *fmt; |
232 | |
233 | if (x == 0) |
234 | return; |
235 | |
236 | code = GET_CODE (x); |
237 | switch (code) |
238 | { |
239 | case PC: |
240 | case CONST_INT: |
241 | case SYMBOL_REF: |
242 | return; |
243 | |
244 | case MATCH_OPERAND: |
245 | case MATCH_SCRATCH: |
246 | VEC_safe_set_locstr (info, vp: &acc->oplocs, XINT (x, 0), |
247 | str: VEC_char_to_string (v: acc->pathstr)); |
248 | break; |
249 | |
250 | case MATCH_OPERATOR: |
251 | case MATCH_PARALLEL: |
252 | VEC_safe_set_locstr (info, vp: &acc->oplocs, XINT (x, 0), |
253 | str: VEC_char_to_string (v: acc->pathstr)); |
254 | |
255 | for (i = XVECLEN (x, 2) - 1; i >= 0; i--) |
256 | { |
257 | push_pathstr_operand (operand: i, is_vector: code != MATCH_OPERATOR, acc); |
258 | walk_rtx (info, XVECEXP (x, 2, i), acc); |
259 | acc->pathstr.pop (); |
260 | } |
261 | return; |
262 | |
263 | case MATCH_DUP: |
264 | case MATCH_PAR_DUP: |
265 | case MATCH_OP_DUP: |
266 | acc->duplocs.safe_push (obj: VEC_char_to_string (v: acc->pathstr)); |
267 | acc->dupnums.safe_push (XINT (x, 0)); |
268 | |
269 | if (code == MATCH_DUP) |
270 | break; |
271 | |
272 | for (i = XVECLEN (x, 1) - 1; i >= 0; i--) |
273 | { |
274 | push_pathstr_operand (operand: i, is_vector: code != MATCH_OP_DUP, acc); |
275 | walk_rtx (info, XVECEXP (x, 1, i), acc); |
276 | acc->pathstr.pop (); |
277 | } |
278 | return; |
279 | |
280 | default: |
281 | break; |
282 | } |
283 | |
284 | fmt = GET_RTX_FORMAT (code); |
285 | len = GET_RTX_LENGTH (code); |
286 | for (i = 0; i < len; i++) |
287 | { |
288 | if (fmt[i] == 'e' || fmt[i] == 'u') |
289 | { |
290 | push_pathstr_operand (operand: i, is_vector: false, acc); |
291 | walk_rtx (info, XEXP (x, i), acc); |
292 | acc->pathstr.pop (); |
293 | } |
294 | else if (fmt[i] == 'E') |
295 | { |
296 | int j; |
297 | for (j = XVECLEN (x, i) - 1; j >= 0; j--) |
298 | { |
299 | push_pathstr_operand (operand: j, is_vector: true, acc); |
300 | walk_rtx (info, XVECEXP (x, i, j), acc); |
301 | acc->pathstr.pop (); |
302 | } |
303 | } |
304 | } |
305 | } |
306 | |
307 | /* Given a PATH, representing a path down the instruction's |
308 | pattern from the root to a certain point, output code to |
309 | evaluate to the rtx at that point. */ |
310 | |
311 | static void |
312 | print_path (const char *path) |
313 | { |
314 | int len = strlen (s: path); |
315 | int i; |
316 | |
317 | if (len == 0) |
318 | { |
319 | /* Don't emit "pat", since we may try to take the address of it, |
320 | which isn't what is intended. */ |
321 | fputs ("PATTERN (insn)" , stdout); |
322 | return; |
323 | } |
324 | |
325 | /* We first write out the operations (XEXP or XVECEXP) in reverse |
326 | order, then write "pat", then the indices in forward order. */ |
327 | |
328 | for (i = len - 1; i >= 0 ; i--) |
329 | { |
330 | if (ISLOWER (path[i]) || ISUPPER (path[i])) |
331 | fputs ("XVECEXP (" , stdout); |
332 | else if (ISDIGIT (path[i])) |
333 | fputs ("XEXP (" , stdout); |
334 | else |
335 | gcc_unreachable (); |
336 | } |
337 | |
338 | fputs ("pat" , stdout); |
339 | |
340 | for (i = 0; i < len; i++) |
341 | { |
342 | if (ISUPPER (path[i])) |
343 | printf (format: ", 0, %d)" , path[i] - UPPER_OFFSET); |
344 | else if (ISLOWER (path[i])) |
345 | printf (format: ", 0, %d)" , path[i] - 'a'); |
346 | else if (ISDIGIT (path[i])) |
347 | printf (format: ", %d)" , path[i] - '0'); |
348 | else |
349 | gcc_unreachable (); |
350 | } |
351 | } |
352 | |
353 | static void |
354 | (void) |
355 | { |
356 | /* N.B. Code below avoids putting squiggle braces in column 1 inside |
357 | a string, because this confuses some editors' syntax highlighting |
358 | engines. */ |
359 | |
360 | puts (s: "\ |
361 | /* Generated automatically by the program `genextract'\n\ |
362 | from the machine description file `md'. */\n\ |
363 | \n\ |
364 | #define IN_TARGET_CODE 1\n\ |
365 | #include \"config.h\"\n\ |
366 | #include \"system.h\"\n\ |
367 | #include \"coretypes.h\"\n\ |
368 | #include \"tm.h\"\n\ |
369 | #include \"rtl.h\"\n\ |
370 | #include \"insn-config.h\"\n\ |
371 | #include \"recog.h\"\n\ |
372 | #include \"diagnostic-core.h\"\n\ |
373 | \n\ |
374 | /* This variable is used as the \"location\" of any missing operand\n\ |
375 | whose numbers are skipped by a given pattern. */\n\ |
376 | static rtx junk ATTRIBUTE_UNUSED;\n" ); |
377 | |
378 | puts (s: "\ |
379 | void\n\ |
380 | insn_extract (rtx_insn *insn)\n{\n\ |
381 | rtx *ro = recog_data.operand;\n\ |
382 | rtx **ro_loc = recog_data.operand_loc;\n\ |
383 | rtx pat = PATTERN (insn);\n\ |
384 | int i ATTRIBUTE_UNUSED; /* only for peepholes */\n\ |
385 | \n\ |
386 | if (flag_checking)\n\ |
387 | {\n\ |
388 | memset (ro, 0xab, sizeof (*ro) * MAX_RECOG_OPERANDS);\n\ |
389 | memset (ro_loc, 0xab, sizeof (*ro_loc) * MAX_RECOG_OPERANDS);\n\ |
390 | }\n" ); |
391 | |
392 | puts (s: "\ |
393 | switch (INSN_CODE (insn))\n\ |
394 | {\n\ |
395 | default:\n\ |
396 | /* Control reaches here if insn_extract has been called with an\n\ |
397 | unrecognizable insn (code -1), or an insn whose INSN_CODE\n\ |
398 | corresponds to a DEFINE_EXPAND in the machine description;\n\ |
399 | either way, a bug. */\n\ |
400 | if (INSN_CODE (insn) < 0)\n\ |
401 | fatal_insn (\"unrecognizable insn:\", insn);\n\ |
402 | else\n\ |
403 | fatal_insn (\"insn with invalid code number:\", insn);\n" ); |
404 | } |
405 | |
406 | int |
407 | main (int argc, const char **argv) |
408 | { |
409 | unsigned int i; |
410 | struct extraction *p; |
411 | struct code_ptr *link; |
412 | const char *name; |
413 | |
414 | progname = "genextract" ; |
415 | |
416 | if (!init_rtx_reader_args (argc, argv)) |
417 | return (FATAL_EXIT_CODE); |
418 | |
419 | /* Read the machine description. */ |
420 | |
421 | md_rtx_info info; |
422 | while (read_md_rtx (&info)) |
423 | switch (GET_CODE (info.def)) |
424 | { |
425 | case DEFINE_INSN: |
426 | gen_insn (info: &info); |
427 | break; |
428 | |
429 | case DEFINE_PEEPHOLE: |
430 | { |
431 | struct code_ptr *link = XNEW (struct code_ptr); |
432 | |
433 | link->insn_code = info.index; |
434 | link->next = peepholes; |
435 | peepholes = link; |
436 | } |
437 | break; |
438 | |
439 | default: |
440 | break; |
441 | } |
442 | |
443 | if (have_error) |
444 | return FATAL_EXIT_CODE; |
445 | |
446 | print_header (); |
447 | |
448 | /* Write out code to handle peepholes and the insn_codes that it should |
449 | be called for. */ |
450 | if (peepholes) |
451 | { |
452 | for (link = peepholes; link; link = link->next) |
453 | printf (format: " case %d:\n" , link->insn_code); |
454 | |
455 | /* The vector in the insn says how many operands it has. |
456 | And all it contains are operands. In fact, the vector was |
457 | created just for the sake of this function. We need to set the |
458 | location of the operands for sake of simplifications after |
459 | extraction, like eliminating subregs. */ |
460 | puts (s: " for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)\n" |
461 | " ro[i] = *(ro_loc[i] = &XVECEXP (pat, 0, i));\n" |
462 | " break;\n" ); |
463 | } |
464 | |
465 | /* Write out all the ways to extract insn operands. */ |
466 | for (p = extractions; p; p = p->next) |
467 | { |
468 | for (link = p->insns; link; link = link->next) |
469 | { |
470 | i = link->insn_code; |
471 | name = get_insn_name (i); |
472 | if (name) |
473 | printf (format: " case %d: /* %s */\n" , i, name); |
474 | else |
475 | printf (format: " case %d:\n" , i); |
476 | } |
477 | |
478 | for (i = 0; i < p->op_count; i++) |
479 | { |
480 | if (p->oplocs[i] == 0) |
481 | { |
482 | printf (format: " ro[%d] = const0_rtx;\n" , i); |
483 | printf (format: " ro_loc[%d] = &junk;\n" , i); |
484 | } |
485 | else |
486 | { |
487 | printf (format: " ro[%d] = *(ro_loc[%d] = &" , i, i); |
488 | print_path (path: p->oplocs[i]); |
489 | puts (s: ");" ); |
490 | } |
491 | } |
492 | |
493 | for (i = 0; i < p->dup_count; i++) |
494 | { |
495 | printf (format: " recog_data.dup_loc[%d] = &" , i); |
496 | print_path (path: p->duplocs[i]); |
497 | puts (s: ";" ); |
498 | printf (format: " recog_data.dup_num[%d] = %d;\n" , i, p->dupnums[i]); |
499 | } |
500 | |
501 | puts (s: " break;\n" ); |
502 | } |
503 | |
504 | puts (s: " }\n}" ); |
505 | fflush (stdout); |
506 | return (ferror (stdout) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); |
507 | } |
508 | |