1 | /* Generate code from machine description to emit insns as rtl. |
2 | Copyright (C) 1987-2023 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 | |
31 | /* Data structure for recording the patterns of insns that have CLOBBERs. |
32 | We use this to output a function that adds these CLOBBERs to a |
33 | previously-allocated PARALLEL expression. */ |
34 | |
35 | struct clobber_pat |
36 | { |
37 | struct clobber_ent *insns; |
38 | rtx pattern; |
39 | int first_clobber; |
40 | struct clobber_pat *next; |
41 | int has_hard_reg; |
42 | } *clobber_list; |
43 | |
44 | /* Records one insn that uses the clobber list. */ |
45 | |
46 | struct clobber_ent |
47 | { |
48 | int code_number; /* Counts only insns. */ |
49 | struct clobber_ent *next; |
50 | }; |
51 | |
52 | static void output_peephole2_scratches (rtx, FILE*); |
53 | |
54 | /* True for <X>_optab if that optab isn't allowed to fail. */ |
55 | static bool nofail_optabs[NUM_OPTABS]; |
56 | |
57 | static void |
58 | print_code (RTX_CODE code, FILE *file) |
59 | { |
60 | const char *p1; |
61 | for (p1 = GET_RTX_NAME (code); *p1; p1++) |
62 | fprintf (stream: file, format: "%c" , TOUPPER (*p1)); |
63 | } |
64 | |
65 | static void |
66 | gen_rtx_scratch (rtx x, enum rtx_code subroutine_type, FILE *file) |
67 | { |
68 | if (subroutine_type == DEFINE_PEEPHOLE2) |
69 | { |
70 | fprintf (stream: file, format: "operand%d" , XINT (x, 0)); |
71 | } |
72 | else |
73 | { |
74 | fprintf (stream: file, format: "gen_rtx_SCRATCH (%smode)" , GET_MODE_NAME (GET_MODE (x))); |
75 | } |
76 | } |
77 | |
78 | /* Print a C expression to construct an RTX just like X, |
79 | substituting any operand references appearing within. */ |
80 | |
81 | static void |
82 | gen_exp (rtx x, enum rtx_code subroutine_type, char *used, md_rtx_info *info, |
83 | FILE *file) |
84 | { |
85 | RTX_CODE code; |
86 | int i; |
87 | int len; |
88 | const char *fmt; |
89 | const char *sep = "" ; |
90 | |
91 | if (x == 0) |
92 | { |
93 | fprintf (stream: file, format: "NULL_RTX" ); |
94 | return; |
95 | } |
96 | |
97 | code = GET_CODE (x); |
98 | |
99 | switch (code) |
100 | { |
101 | case MATCH_OPERAND: |
102 | case MATCH_DUP: |
103 | if (used) |
104 | { |
105 | if (used[XINT (x, 0)]) |
106 | { |
107 | fprintf (stream: file, format: "copy_rtx (operand%d)" , XINT (x, 0)); |
108 | return; |
109 | } |
110 | used[XINT (x, 0)] = 1; |
111 | } |
112 | fprintf (stream: file, format: "operand%d" , XINT (x, 0)); |
113 | return; |
114 | |
115 | case MATCH_OP_DUP: |
116 | fprintf (stream: file, format: "gen_rtx_fmt_" ); |
117 | for (i = 0; i < XVECLEN (x, 1); i++) |
118 | fprintf (stream: file, format: "e" ); |
119 | fprintf (stream: file, format: " (GET_CODE (operand%d), " , XINT (x, 0)); |
120 | if (GET_MODE (x) == VOIDmode) |
121 | fprintf (stream: file, format: "GET_MODE (operand%d)" , XINT (x, 0)); |
122 | else |
123 | fprintf (stream: file, format: "%smode" , GET_MODE_NAME (GET_MODE (x))); |
124 | for (i = 0; i < XVECLEN (x, 1); i++) |
125 | { |
126 | fprintf (stream: file, format: ",\n\t\t" ); |
127 | gen_exp (XVECEXP (x, 1, i), subroutine_type, used, info, file); |
128 | } |
129 | fprintf (stream: file, format: ")" ); |
130 | return; |
131 | |
132 | case MATCH_OPERATOR: |
133 | fprintf (stream: file, format: "gen_rtx_fmt_" ); |
134 | for (i = 0; i < XVECLEN (x, 2); i++) |
135 | fprintf (stream: file, format: "e" ); |
136 | fprintf (stream: file, format: " (GET_CODE (operand%d)" , XINT (x, 0)); |
137 | fprintf (stream: file, format: ", %smode" , GET_MODE_NAME (GET_MODE (x))); |
138 | for (i = 0; i < XVECLEN (x, 2); i++) |
139 | { |
140 | fprintf (stream: file, format: ",\n\t\t" ); |
141 | gen_exp (XVECEXP (x, 2, i), subroutine_type, used, info, file); |
142 | } |
143 | fprintf (stream: file, format: ")" ); |
144 | return; |
145 | |
146 | case MATCH_PARALLEL: |
147 | case MATCH_PAR_DUP: |
148 | fprintf (stream: file, format: "operand%d" , XINT (x, 0)); |
149 | return; |
150 | |
151 | case MATCH_SCRATCH: |
152 | gen_rtx_scratch (x, subroutine_type, file); |
153 | return; |
154 | |
155 | case PC: |
156 | fprintf (stream: file, format: "pc_rtx" ); |
157 | return; |
158 | case RETURN: |
159 | fprintf (stream: file, format: "ret_rtx" ); |
160 | return; |
161 | case SIMPLE_RETURN: |
162 | fprintf (stream: file, format: "simple_return_rtx" ); |
163 | return; |
164 | case CLOBBER: |
165 | if (REG_P (XEXP (x, 0))) |
166 | { |
167 | fprintf (stream: file, format: "gen_hard_reg_clobber (%smode, %i)" , |
168 | GET_MODE_NAME (GET_MODE (XEXP (x, 0))), |
169 | REGNO (XEXP (x, 0))); |
170 | return; |
171 | } |
172 | break; |
173 | |
174 | case CONST_INT: |
175 | if (INTVAL (x) == 0) |
176 | fprintf (stream: file, format: "const0_rtx" ); |
177 | else if (INTVAL (x) == 1) |
178 | fprintf (stream: file, format: "const1_rtx" ); |
179 | else if (INTVAL (x) == -1) |
180 | fprintf (stream: file, format: "constm1_rtx" ); |
181 | else if (-MAX_SAVED_CONST_INT <= INTVAL (x) |
182 | && INTVAL (x) <= MAX_SAVED_CONST_INT) |
183 | fprintf (stream: file, format: "const_int_rtx[MAX_SAVED_CONST_INT + (%d)]" , |
184 | (int) INTVAL (x)); |
185 | else if (INTVAL (x) == STORE_FLAG_VALUE) |
186 | fprintf (stream: file, format: "const_true_rtx" ); |
187 | else |
188 | { |
189 | fprintf (stream: file, format: "GEN_INT (" ); |
190 | fprintf (stream: file, HOST_WIDE_INT_PRINT_DEC_C, INTVAL (x)); |
191 | fprintf (stream: file, format: ")" ); |
192 | } |
193 | return; |
194 | |
195 | case CONST_DOUBLE: |
196 | /* Handle `const_double_zero' rtx. */ |
197 | if (CONST_DOUBLE_REAL_VALUE (x)->cl == rvc_zero) |
198 | { |
199 | fprintf (stream: file, format: "CONST_DOUBLE_ATOF (\"0\", %smode)" , |
200 | GET_MODE_NAME (GET_MODE (x))); |
201 | return; |
202 | } |
203 | /* Fall through. */ |
204 | case CONST_FIXED: |
205 | case CONST_WIDE_INT: |
206 | /* These shouldn't be written in MD files. Instead, the appropriate |
207 | routines in varasm.cc should be called. */ |
208 | gcc_unreachable (); |
209 | |
210 | default: |
211 | break; |
212 | } |
213 | |
214 | fprintf (stream: file, format: "gen_rtx_" ); |
215 | print_code (code, file); |
216 | fprintf (stream: file, format: " (" ); |
217 | if (!always_void_p (code)) |
218 | { |
219 | fprintf (stream: file, format: "%smode" , GET_MODE_NAME (GET_MODE (x))); |
220 | sep = ",\n\t" ; |
221 | } |
222 | |
223 | fmt = GET_RTX_FORMAT (code); |
224 | len = GET_RTX_LENGTH (code); |
225 | for (i = 0; i < len; i++) |
226 | { |
227 | if (fmt[i] == '0') |
228 | break; |
229 | fputs (sep, file); |
230 | switch (fmt[i]) |
231 | { |
232 | case 'e': case 'u': |
233 | gen_exp (XEXP (x, i), subroutine_type, used, info, file); |
234 | break; |
235 | |
236 | case 'i': |
237 | fprintf (stream: file, format: "%u" , XINT (x, i)); |
238 | break; |
239 | |
240 | case 'r': |
241 | fprintf (stream: file, format: "%u" , REGNO (x)); |
242 | break; |
243 | |
244 | case 'p': |
245 | /* We don't have a way of parsing polynomial offsets yet, |
246 | and hopefully never will. */ |
247 | fprintf (stream: file, format: "%d" , SUBREG_BYTE (x).to_constant ()); |
248 | break; |
249 | |
250 | case 's': |
251 | fprintf (stream: file, format: "\"%s\"" , XSTR (x, i)); |
252 | break; |
253 | |
254 | case 'E': |
255 | { |
256 | int j; |
257 | fprintf (stream: file, format: "gen_rtvec (%d" , XVECLEN (x, i)); |
258 | for (j = 0; j < XVECLEN (x, i); j++) |
259 | { |
260 | fprintf (stream: file, format: ",\n\t\t" ); |
261 | gen_exp (XVECEXP (x, i, j), subroutine_type, used, info, file); |
262 | } |
263 | fprintf (stream: file, format: ")" ); |
264 | break; |
265 | } |
266 | |
267 | default: |
268 | gcc_unreachable (); |
269 | } |
270 | sep = ",\n\t" ; |
271 | } |
272 | fprintf (stream: file, format: ")" ); |
273 | } |
274 | |
275 | /* Output code to emit the instruction patterns in VEC, with each element |
276 | becoming a separate instruction. USED is as for gen_exp. */ |
277 | |
278 | static void |
279 | gen_emit_seq (rtvec vec, char *used, md_rtx_info *info, FILE *file) |
280 | { |
281 | for (int i = 0, len = GET_NUM_ELEM (vec); i < len; ++i) |
282 | { |
283 | bool last_p = (i == len - 1); |
284 | rtx next = RTVEC_ELT (vec, i); |
285 | if (const char *name = get_emit_function (next)) |
286 | { |
287 | fprintf (stream: file, format: " %s (" , name); |
288 | gen_exp (x: next, subroutine_type: DEFINE_EXPAND, used, info, file); |
289 | fprintf (stream: file, format: ");\n" ); |
290 | if (!last_p && needs_barrier_p (next)) |
291 | fprintf (stream: file, format: " emit_barrier ();" ); |
292 | } |
293 | else |
294 | { |
295 | fprintf (stream: file, format: " emit (" ); |
296 | gen_exp (x: next, subroutine_type: DEFINE_EXPAND, used, info, file); |
297 | fprintf (stream: file, format: ", %s);\n" , last_p ? "false" : "true" ); |
298 | } |
299 | } |
300 | } |
301 | |
302 | /* Emit the given C code to the output file. The code is allowed to |
303 | fail if CAN_FAIL_P. NAME describes what we're generating, |
304 | for use in error messages. */ |
305 | |
306 | static void |
307 | emit_c_code (const char *code, bool can_fail_p, const char *name, FILE *file) |
308 | { |
309 | if (can_fail_p) |
310 | fprintf (stream: file, format: "#define FAIL return (end_sequence (), _val)\n" ); |
311 | else |
312 | fprintf (stream: file, format: "#define FAIL _Pragma (\"GCC error \\\"%s cannot FAIL\\\"\")" |
313 | " (void)0\n" , name); |
314 | fprintf (stream: file, format: "#define DONE return (_val = get_insns (), " |
315 | "end_sequence (), _val)\n" ); |
316 | |
317 | rtx_reader_ptr->print_md_ptr_loc (ptr: code, file); |
318 | fprintf (stream: file, format: "%s\n" , code); |
319 | |
320 | fprintf (stream: file, format: "#undef DONE\n" ); |
321 | fprintf (stream: file, format: "#undef FAIL\n" ); |
322 | } |
323 | |
324 | /* Generate the `gen_...' function for a DEFINE_INSN. */ |
325 | |
326 | static void |
327 | gen_insn (md_rtx_info *info, FILE *file) |
328 | { |
329 | struct pattern_stats stats; |
330 | int i; |
331 | |
332 | /* See if the pattern for this insn ends with a group of CLOBBERs of (hard) |
333 | registers or MATCH_SCRATCHes. If so, store away the information for |
334 | later. */ |
335 | |
336 | rtx insn = info->def; |
337 | if (XVEC (insn, 1)) |
338 | { |
339 | int has_hard_reg = 0; |
340 | |
341 | for (i = XVECLEN (insn, 1) - 1; i > 0; i--) |
342 | { |
343 | if (GET_CODE (XVECEXP (insn, 1, i)) != CLOBBER) |
344 | break; |
345 | |
346 | if (REG_P (XEXP (XVECEXP (insn, 1, i), 0))) |
347 | has_hard_reg = 1; |
348 | else if (GET_CODE (XEXP (XVECEXP (insn, 1, i), 0)) != MATCH_SCRATCH) |
349 | break; |
350 | } |
351 | |
352 | if (i != XVECLEN (insn, 1) - 1) |
353 | { |
354 | struct clobber_pat *p; |
355 | struct clobber_ent *link = XNEW (struct clobber_ent); |
356 | int j; |
357 | |
358 | link->code_number = info->index; |
359 | |
360 | /* See if any previous CLOBBER_LIST entry is the same as this |
361 | one. */ |
362 | |
363 | for (p = clobber_list; p; p = p->next) |
364 | { |
365 | if (p->first_clobber != i + 1 |
366 | || XVECLEN (p->pattern, 1) != XVECLEN (insn, 1)) |
367 | continue; |
368 | |
369 | for (j = i + 1; j < XVECLEN (insn, 1); j++) |
370 | { |
371 | rtx old_rtx = XEXP (XVECEXP (p->pattern, 1, j), 0); |
372 | rtx new_rtx = XEXP (XVECEXP (insn, 1, j), 0); |
373 | |
374 | /* OLD and NEW_INSN are the same if both are to be a SCRATCH |
375 | of the same mode, |
376 | or if both are registers of the same mode and number. */ |
377 | if (! (GET_CODE (old_rtx) == GET_CODE (new_rtx) |
378 | && GET_MODE (old_rtx) == GET_MODE (new_rtx) |
379 | && ((GET_CODE (old_rtx) == MATCH_SCRATCH |
380 | && GET_CODE (new_rtx) == MATCH_SCRATCH) |
381 | || (REG_P (old_rtx) && REG_P (new_rtx) |
382 | && REGNO (old_rtx) == REGNO (new_rtx))))) |
383 | break; |
384 | } |
385 | |
386 | if (j == XVECLEN (insn, 1)) |
387 | break; |
388 | } |
389 | |
390 | if (p == 0) |
391 | { |
392 | p = XNEW (struct clobber_pat); |
393 | |
394 | p->insns = 0; |
395 | p->pattern = insn; |
396 | p->first_clobber = i + 1; |
397 | p->next = clobber_list; |
398 | p->has_hard_reg = has_hard_reg; |
399 | clobber_list = p; |
400 | } |
401 | |
402 | link->next = p->insns; |
403 | p->insns = link; |
404 | } |
405 | } |
406 | |
407 | /* Don't mention instructions whose names are the null string |
408 | or begin with '*'. They are in the machine description just |
409 | to be recognized. */ |
410 | if (XSTR (insn, 0)[0] == 0 || XSTR (insn, 0)[0] == '*') |
411 | return; |
412 | |
413 | fprintf (stream: file, format: "/* %s:%d */\n" , info->loc.filename, info->loc.lineno); |
414 | |
415 | /* Find out how many operands this function has. */ |
416 | get_pattern_stats (ranges: &stats, XVEC (insn, 1)); |
417 | if (stats.max_dup_opno > stats.max_opno) |
418 | fatal_at (info->loc, "match_dup operand number has no match_operand" ); |
419 | |
420 | /* Output the function name and argument declarations. */ |
421 | fprintf (stream: file, format: "rtx\ngen_%s (" , XSTR (insn, 0)); |
422 | if (stats.num_generator_args) |
423 | for (i = 0; i < stats.num_generator_args; i++) |
424 | if (i) |
425 | fprintf (stream: file, format: ",\n\trtx operand%d ATTRIBUTE_UNUSED" , i); |
426 | else |
427 | fprintf (stream: file, format: "rtx operand%d ATTRIBUTE_UNUSED" , i); |
428 | else |
429 | fprintf (stream: file, format: "void" ); |
430 | fprintf (stream: file, format: ")\n" ); |
431 | fprintf (stream: file, format: "{\n" ); |
432 | |
433 | /* Output code to construct and return the rtl for the instruction body. */ |
434 | |
435 | rtx pattern = add_implicit_parallel (XVEC (insn, 1)); |
436 | /* ??? This is the traditional behavior, but seems suspect. */ |
437 | char *used = (XVECLEN (insn, 1) == 1 |
438 | ? NULL |
439 | : XCNEWVEC (char, stats.num_generator_args)); |
440 | fprintf (stream: file, format: " return " ); |
441 | gen_exp (x: pattern, subroutine_type: DEFINE_INSN, used, info, file); |
442 | fprintf (stream: file, format: ";\n}\n\n" ); |
443 | XDELETEVEC (used); |
444 | } |
445 | |
446 | /* Generate the `gen_...' function for a DEFINE_EXPAND. */ |
447 | |
448 | static void |
449 | gen_expand (md_rtx_info *info, FILE *file) |
450 | { |
451 | struct pattern_stats stats; |
452 | int i; |
453 | char *used; |
454 | |
455 | rtx expand = info->def; |
456 | if (strlen (XSTR (expand, 0)) == 0) |
457 | fatal_at (info->loc, "define_expand lacks a name" ); |
458 | if (XVEC (expand, 1) == 0) |
459 | fatal_at (info->loc, "define_expand for %s lacks a pattern" , |
460 | XSTR (expand, 0)); |
461 | |
462 | /* Find out how many operands this function has. */ |
463 | get_pattern_stats (ranges: &stats, XVEC (expand, 1)); |
464 | if (stats.min_scratch_opno != -1 |
465 | && stats.min_scratch_opno <= MAX (stats.max_opno, stats.max_dup_opno)) |
466 | fatal_at (info->loc, "define_expand for %s needs to have match_scratch " |
467 | "numbers above all other operands" , XSTR (expand, 0)); |
468 | |
469 | /* Output the function name and argument declarations. */ |
470 | fprintf (stream: file, format: "rtx\ngen_%s (" , XSTR (expand, 0)); |
471 | if (stats.num_generator_args) |
472 | for (i = 0; i < stats.num_generator_args; i++) |
473 | if (i) |
474 | fprintf (stream: file, format: ",\n\trtx operand%d" , i); |
475 | else |
476 | fprintf (stream: file, format: "rtx operand%d" , i); |
477 | else |
478 | fprintf (stream: file, format: "void" ); |
479 | fprintf (stream: file, format: ")\n" ); |
480 | fprintf (stream: file, format: "{\n" ); |
481 | |
482 | /* If we don't have any C code to write, only one insn is being written, |
483 | and no MATCH_DUPs are present, we can just return the desired insn |
484 | like we do for a DEFINE_INSN. This saves memory. */ |
485 | if ((XSTR (expand, 3) == 0 || *XSTR (expand, 3) == '\0') |
486 | && stats.max_opno >= stats.max_dup_opno |
487 | && XVECLEN (expand, 1) == 1) |
488 | { |
489 | fprintf (stream: file, format: " return " ); |
490 | gen_exp (XVECEXP (expand, 1, 0), subroutine_type: DEFINE_EXPAND, NULL, info, file); |
491 | fprintf (stream: file, format: ";\n}\n\n" ); |
492 | return; |
493 | } |
494 | |
495 | /* For each operand referred to only with MATCH_DUPs, |
496 | make a local variable. */ |
497 | for (i = stats.num_generator_args; i <= stats.max_dup_opno; i++) |
498 | fprintf (stream: file, format: " rtx operand%d;\n" , i); |
499 | fprintf (stream: file, format: " rtx_insn *_val = 0;\n" ); |
500 | fprintf (stream: file, format: " start_sequence ();\n" ); |
501 | |
502 | /* The fourth operand of DEFINE_EXPAND is some code to be executed |
503 | before the actual construction. |
504 | This code expects to refer to `operands' |
505 | just as the output-code in a DEFINE_INSN does, |
506 | but here `operands' is an automatic array. |
507 | So copy the operand values there before executing it. */ |
508 | if (XSTR (expand, 3) && *XSTR (expand, 3)) |
509 | { |
510 | fprintf (stream: file, format: " {\n" ); |
511 | if (stats.num_operand_vars > 0) |
512 | fprintf (stream: file, format: " rtx operands[%d];\n" , stats.num_operand_vars); |
513 | |
514 | /* Output code to copy the arguments into `operands'. */ |
515 | for (i = 0; i < stats.num_generator_args; i++) |
516 | fprintf (stream: file, format: " operands[%d] = operand%d;\n" , i, i); |
517 | |
518 | /* Output the special code to be executed before the sequence |
519 | is generated. */ |
520 | optab_pattern p; |
521 | bool can_fail_p = true; |
522 | if (find_optab (&p, XSTR (expand, 0))) |
523 | { |
524 | gcc_assert (p.op < NUM_OPTABS); |
525 | if (nofail_optabs[p.op]) |
526 | can_fail_p = false; |
527 | } |
528 | emit_c_code (XSTR (expand, 3), can_fail_p, XSTR (expand, 0), file); |
529 | |
530 | /* Output code to copy the arguments back out of `operands' |
531 | (unless we aren't going to use them at all). */ |
532 | if (XVEC (expand, 1) != 0) |
533 | { |
534 | for (i = 0; i <= MAX (stats.max_opno, stats.max_dup_opno); i++) |
535 | { |
536 | fprintf (stream: file, format: " operand%d = operands[%d];\n" , i, i); |
537 | fprintf (stream: file, format: " (void) operand%d;\n" , i); |
538 | } |
539 | } |
540 | fprintf (stream: file, format: " }\n" ); |
541 | } |
542 | |
543 | used = XCNEWVEC (char, stats.num_operand_vars); |
544 | gen_emit_seq (XVEC (expand, 1), used, info, file); |
545 | XDELETEVEC (used); |
546 | |
547 | /* Call `get_insns' to extract the list of all the |
548 | insns emitted within this gen_... function. */ |
549 | |
550 | fprintf (stream: file, format: " _val = get_insns ();\n" ); |
551 | fprintf (stream: file, format: " end_sequence ();\n" ); |
552 | fprintf (stream: file, format: " return _val;\n}\n\n" ); |
553 | } |
554 | |
555 | /* Like gen_expand, but generates insns resulting from splitting SPLIT. */ |
556 | |
557 | static void |
558 | gen_split (md_rtx_info *info, FILE *file) |
559 | { |
560 | struct pattern_stats stats; |
561 | int i; |
562 | rtx split = info->def; |
563 | const char *const name = |
564 | ((GET_CODE (split) == DEFINE_PEEPHOLE2) ? "peephole2" : "split" ); |
565 | const char *unused; |
566 | char *used; |
567 | |
568 | if (XVEC (split, 0) == 0) |
569 | fatal_at (info->loc, "%s lacks a pattern" , |
570 | GET_RTX_NAME (GET_CODE (split))); |
571 | else if (XVEC (split, 2) == 0) |
572 | fatal_at (info->loc, "%s lacks a replacement pattern" , |
573 | GET_RTX_NAME (GET_CODE (split))); |
574 | |
575 | /* Find out how many operands this function has. */ |
576 | |
577 | get_pattern_stats (ranges: &stats, XVEC (split, 2)); |
578 | unused = (stats.num_operand_vars == 0 ? " ATTRIBUTE_UNUSED" : "" ); |
579 | used = XCNEWVEC (char, stats.num_operand_vars); |
580 | |
581 | /* Output the prototype, function name and argument declarations. */ |
582 | if (GET_CODE (split) == DEFINE_PEEPHOLE2) |
583 | { |
584 | fprintf (stream: file, format: "extern rtx_insn *gen_%s_%d (rtx_insn *, rtx *);\n" , |
585 | name, info->index); |
586 | fprintf (stream: file, format: "rtx_insn *\ngen_%s_%d (rtx_insn *curr_insn ATTRIBUTE_UNUSED," |
587 | " rtx *operands%s)\n" , |
588 | name, info->index, unused); |
589 | } |
590 | else |
591 | { |
592 | fprintf (stream: file, format: "extern rtx_insn *gen_split_%d (rtx_insn *, rtx *);\n" , |
593 | info->index); |
594 | fprintf (stream: file, format: "rtx_insn *\ngen_split_%d " |
595 | "(rtx_insn *curr_insn ATTRIBUTE_UNUSED, rtx *operands%s)\n" , |
596 | info->index, unused); |
597 | } |
598 | fprintf (stream: file, format: "{\n" ); |
599 | |
600 | /* Declare all local variables. */ |
601 | for (i = 0; i < stats.num_operand_vars; i++) |
602 | fprintf (stream: file, format: " rtx operand%d;\n" , i); |
603 | fprintf (stream: file, format: " rtx_insn *_val = NULL;\n" ); |
604 | |
605 | if (GET_CODE (split) == DEFINE_PEEPHOLE2) |
606 | output_peephole2_scratches (split, file); |
607 | |
608 | const char *fn = info->loc.filename; |
609 | for (const char *p = fn; *p; p++) |
610 | if (*p == '/') |
611 | fn = p + 1; |
612 | |
613 | fprintf (stream: file, format: " if (dump_file)\n" ); |
614 | fprintf (stream: file, format: " fprintf (dump_file, \"Splitting with gen_%s_%d (%s:%d)\\n\");\n" , |
615 | name, info->index, fn, info->loc.lineno); |
616 | |
617 | fprintf (stream: file, format: " start_sequence ();\n" ); |
618 | |
619 | /* The fourth operand of DEFINE_SPLIT is some code to be executed |
620 | before the actual construction. */ |
621 | |
622 | if (XSTR (split, 3)) |
623 | emit_c_code (XSTR (split, 3), can_fail_p: true, name, file); |
624 | |
625 | /* Output code to copy the arguments back out of `operands' */ |
626 | for (i = 0; i < stats.num_operand_vars; i++) |
627 | { |
628 | fprintf (stream: file, format: " operand%d = operands[%d];\n" , i, i); |
629 | fprintf (stream: file, format: " (void) operand%d;\n" , i); |
630 | } |
631 | |
632 | gen_emit_seq (XVEC (split, 2), used, info, file); |
633 | |
634 | /* Call `get_insns' to make a list of all the |
635 | insns emitted within this gen_... function. */ |
636 | |
637 | fprintf (stream: file, format: " _val = get_insns ();\n" ); |
638 | fprintf (stream: file, format: " end_sequence ();\n" ); |
639 | fprintf (stream: file, format: " return _val;\n}\n\n" ); |
640 | |
641 | free (ptr: used); |
642 | } |
643 | |
644 | /* Write a function, `add_clobbers', that is given a PARALLEL of sufficient |
645 | size for the insn and an INSN_CODE, and inserts the required CLOBBERs at |
646 | the end of the vector. */ |
647 | |
648 | static void |
649 | output_add_clobbers (md_rtx_info *info, FILE *file) |
650 | { |
651 | struct clobber_pat *clobber; |
652 | struct clobber_ent *ent; |
653 | int i; |
654 | |
655 | fprintf (stream: file, format: "\n\nvoid\nadd_clobbers (rtx pattern ATTRIBUTE_UNUSED, int insn_code_number)\n" ); |
656 | fprintf (stream: file, format: "{\n" ); |
657 | fprintf (stream: file, format: " switch (insn_code_number)\n" ); |
658 | fprintf (stream: file, format: " {\n" ); |
659 | |
660 | for (clobber = clobber_list; clobber; clobber = clobber->next) |
661 | { |
662 | for (ent = clobber->insns; ent; ent = ent->next) |
663 | fprintf (stream: file, format: " case %d:\n" , ent->code_number); |
664 | |
665 | for (i = clobber->first_clobber; i < XVECLEN (clobber->pattern, 1); i++) |
666 | { |
667 | fprintf (stream: file, format: " XVECEXP (pattern, 0, %d) = " , i); |
668 | gen_exp (XVECEXP (clobber->pattern, 1, i), |
669 | GET_CODE (clobber->pattern), NULL, info, file); |
670 | fprintf (stream: file, format: ";\n" ); |
671 | } |
672 | |
673 | fprintf (stream: file, format: " break;\n\n" ); |
674 | } |
675 | |
676 | fprintf (stream: file, format: " default:\n" ); |
677 | fprintf (stream: file, format: " gcc_unreachable ();\n" ); |
678 | fprintf (stream: file, format: " }\n" ); |
679 | fprintf (stream: file, format: "}\n" ); |
680 | } |
681 | |
682 | /* Write a function, `added_clobbers_hard_reg_p' that is given an insn_code |
683 | number that will have clobbers added (as indicated by `recog') and returns |
684 | 1 if those include a clobber of a hard reg or 0 if all of them just clobber |
685 | SCRATCH. */ |
686 | |
687 | static void |
688 | output_added_clobbers_hard_reg_p (FILE *file) |
689 | { |
690 | struct clobber_pat *clobber; |
691 | struct clobber_ent *ent; |
692 | int clobber_p; |
693 | bool used; |
694 | |
695 | fprintf (stream: file, format: "\n\nbool\nadded_clobbers_hard_reg_p (int insn_code_number)\n" ); |
696 | fprintf (stream: file, format: "{\n" ); |
697 | fprintf (stream: file, format: " switch (insn_code_number)\n" ); |
698 | fprintf (stream: file, format: " {\n" ); |
699 | |
700 | for (clobber_p = 0; clobber_p <= 1; clobber_p++) |
701 | { |
702 | used = false; |
703 | for (clobber = clobber_list; clobber; clobber = clobber->next) |
704 | if (clobber->has_hard_reg == clobber_p) |
705 | for (ent = clobber->insns; ent; ent = ent->next) |
706 | { |
707 | fprintf (stream: file, format: " case %d:\n" , ent->code_number); |
708 | used = true; |
709 | } |
710 | |
711 | if (used) |
712 | fprintf (stream: file, format: " return %s;\n\n" , clobber_p ? "true" : "false" ); |
713 | } |
714 | |
715 | fprintf (stream: file, format: " default:\n" ); |
716 | fprintf (stream: file, format: " gcc_unreachable ();\n" ); |
717 | fprintf (stream: file, format: " }\n" ); |
718 | fprintf (stream: file, format: "}\n" ); |
719 | } |
720 | |
721 | /* Generate code to invoke find_free_register () as needed for the |
722 | scratch registers used by the peephole2 pattern in SPLIT. */ |
723 | |
724 | static void |
725 | output_peephole2_scratches (rtx split, FILE *file) |
726 | { |
727 | int i; |
728 | int insn_nr = 0; |
729 | bool first = true; |
730 | |
731 | for (i = 0; i < XVECLEN (split, 0); i++) |
732 | { |
733 | rtx elt = XVECEXP (split, 0, i); |
734 | if (GET_CODE (elt) == MATCH_SCRATCH) |
735 | { |
736 | int last_insn_nr = insn_nr; |
737 | int cur_insn_nr = insn_nr; |
738 | int j; |
739 | for (j = i + 1; j < XVECLEN (split, 0); j++) |
740 | if (GET_CODE (XVECEXP (split, 0, j)) == MATCH_DUP) |
741 | { |
742 | if (XINT (XVECEXP (split, 0, j), 0) == XINT (elt, 0)) |
743 | last_insn_nr = cur_insn_nr; |
744 | } |
745 | else if (GET_CODE (XVECEXP (split, 0, j)) != MATCH_SCRATCH) |
746 | cur_insn_nr++; |
747 | |
748 | if (first) |
749 | { |
750 | fprintf (stream: file, format: " HARD_REG_SET _regs_allocated;\n" ); |
751 | fprintf (stream: file, format: " CLEAR_HARD_REG_SET (_regs_allocated);\n" ); |
752 | first = false; |
753 | } |
754 | |
755 | fprintf (stream: file, format: " if ((operands[%d] = peep2_find_free_register (%d, %d, \"%s\", %smode, &_regs_allocated)) == NULL_RTX)\n\ |
756 | return NULL;\n" , |
757 | XINT (elt, 0), |
758 | insn_nr, last_insn_nr, |
759 | XSTR (elt, 1), |
760 | GET_MODE_NAME (GET_MODE (elt))); |
761 | |
762 | } |
763 | else if (GET_CODE (elt) != MATCH_DUP) |
764 | insn_nr++; |
765 | } |
766 | } |
767 | |
768 | /* Print "arg<N>" parameter declarations for each argument N of ONAME. */ |
769 | |
770 | static void |
771 | print_overload_arguments (overloaded_name *oname, FILE *file) |
772 | { |
773 | for (unsigned int i = 0; i < oname->arg_types.length (); ++i) |
774 | fprintf (stream: file, format: "%s%s arg%d" , i == 0 ? "" : ", " , oname->arg_types[i], i); |
775 | } |
776 | |
777 | /* Print code to test whether INSTANCE should be chosen, given that |
778 | argument N of the overload is available as "arg<N>". */ |
779 | |
780 | static void |
781 | print_overload_test (overloaded_instance *instance, FILE *file) |
782 | { |
783 | for (unsigned int i = 0; i < instance->arg_values.length (); ++i) |
784 | fprintf (stream: file, format: "%sarg%d == %s" , i == 0 ? " if (" : "\n && " , |
785 | i, instance->arg_values[i]); |
786 | fprintf (stream: file, format: ")\n" ); |
787 | } |
788 | |
789 | /* Emit a maybe_code_for_* function for ONAME. */ |
790 | |
791 | static void |
792 | handle_overloaded_code_for (overloaded_name *oname, FILE *file) |
793 | { |
794 | /* Print the function prototype. */ |
795 | fprintf (stream: file, format: "\ninsn_code\nmaybe_code_for_%s (" , oname->name); |
796 | print_overload_arguments (oname, file); |
797 | fprintf (stream: file, format: ")\n{\n" ); |
798 | |
799 | /* Use a sequence of "if" statements for each instance. */ |
800 | for (overloaded_instance *instance = oname->first_instance; |
801 | instance; instance = instance->next) |
802 | { |
803 | print_overload_test (instance, file); |
804 | fprintf (stream: file, format: " return CODE_FOR_%s;\n" , instance->name); |
805 | } |
806 | |
807 | /* Return null if no match was found. */ |
808 | fprintf (stream: file, format: " return CODE_FOR_nothing;\n}\n" ); |
809 | } |
810 | |
811 | /* Emit a maybe_gen_* function for ONAME. */ |
812 | |
813 | static void |
814 | handle_overloaded_gen (overloaded_name *oname, FILE *file) |
815 | { |
816 | unsigned HOST_WIDE_INT seen = 0; |
817 | /* All patterns must have the same number of operands. */ |
818 | for (overloaded_instance *instance = oname->first_instance->next; |
819 | instance; instance = instance->next) |
820 | { |
821 | pattern_stats stats; |
822 | get_pattern_stats (ranges: &stats, XVEC (instance->insn, 1)); |
823 | unsigned HOST_WIDE_INT mask |
824 | = HOST_WIDE_INT_1U << stats.num_generator_args; |
825 | if (seen & mask) |
826 | continue; |
827 | |
828 | seen |= mask; |
829 | |
830 | /* Print the function prototype. */ |
831 | fprintf (stream: file, format: "\nrtx\nmaybe_gen_%s (" , oname->name); |
832 | print_overload_arguments (oname, file); |
833 | for (int i = 0; i < stats.num_generator_args; ++i) |
834 | fprintf (stream: file, format: ", rtx x%d" , i); |
835 | fprintf (stream: file, format: ")\n{\n" ); |
836 | |
837 | /* Use maybe_code_for_*, instead of duplicating the selection |
838 | logic here. */ |
839 | fprintf (stream: file, format: " insn_code code = maybe_code_for_%s (" , oname->name); |
840 | for (unsigned int i = 0; i < oname->arg_types.length (); ++i) |
841 | fprintf (stream: file, format: "%sarg%d" , i == 0 ? "" : ", " , i); |
842 | fprintf (stream: file, format: ");\n" |
843 | " if (code != CODE_FOR_nothing)\n" |
844 | " {\n" |
845 | " gcc_assert (insn_data[code].n_generator_args == %d);\n" |
846 | " return GEN_FCN (code) (" , stats.num_generator_args); |
847 | for (int i = 0; i < stats.num_generator_args; ++i) |
848 | fprintf (stream: file, format: "%sx%d" , i == 0 ? "" : ", " , i); |
849 | fprintf (stream: file, format: ");\n" |
850 | " }\n" |
851 | " else\n" |
852 | " return NULL_RTX;\n" |
853 | "}\n" ); |
854 | } |
855 | } |
856 | |
857 | void |
858 | (FILE *file) |
859 | { |
860 | fprintf (stream: file, format: "/* Generated automatically by the program `genemit'\n\ |
861 | from the machine description file `md'. */\n\n" ); |
862 | |
863 | fprintf (stream: file, format: "#define IN_TARGET_CODE 1\n" ); |
864 | fprintf (stream: file, format: "#include \"config.h\"\n" ); |
865 | fprintf (stream: file, format: "#include \"system.h\"\n" ); |
866 | fprintf (stream: file, format: "#include \"coretypes.h\"\n" ); |
867 | fprintf (stream: file, format: "#include \"backend.h\"\n" ); |
868 | fprintf (stream: file, format: "#include \"predict.h\"\n" ); |
869 | fprintf (stream: file, format: "#include \"tree.h\"\n" ); |
870 | fprintf (stream: file, format: "#include \"rtl.h\"\n" ); |
871 | fprintf (stream: file, format: "#include \"alias.h\"\n" ); |
872 | fprintf (stream: file, format: "#include \"varasm.h\"\n" ); |
873 | fprintf (stream: file, format: "#include \"stor-layout.h\"\n" ); |
874 | fprintf (stream: file, format: "#include \"calls.h\"\n" ); |
875 | fprintf (stream: file, format: "#include \"memmodel.h\"\n" ); |
876 | fprintf (stream: file, format: "#include \"tm_p.h\"\n" ); |
877 | fprintf (stream: file, format: "#include \"flags.h\"\n" ); |
878 | fprintf (stream: file, format: "#include \"insn-config.h\"\n" ); |
879 | fprintf (stream: file, format: "#include \"expmed.h\"\n" ); |
880 | fprintf (stream: file, format: "#include \"dojump.h\"\n" ); |
881 | fprintf (stream: file, format: "#include \"explow.h\"\n" ); |
882 | fprintf (stream: file, format: "#include \"emit-rtl.h\"\n" ); |
883 | fprintf (stream: file, format: "#include \"stmt.h\"\n" ); |
884 | fprintf (stream: file, format: "#include \"expr.h\"\n" ); |
885 | fprintf (stream: file, format: "#include \"insn-codes.h\"\n" ); |
886 | fprintf (stream: file, format: "#include \"optabs.h\"\n" ); |
887 | fprintf (stream: file, format: "#include \"dfp.h\"\n" ); |
888 | fprintf (stream: file, format: "#include \"output.h\"\n" ); |
889 | fprintf (stream: file, format: "#include \"recog.h\"\n" ); |
890 | fprintf (stream: file, format: "#include \"df.h\"\n" ); |
891 | fprintf (stream: file, format: "#include \"resource.h\"\n" ); |
892 | fprintf (stream: file, format: "#include \"reload.h\"\n" ); |
893 | fprintf (stream: file, format: "#include \"diagnostic-core.h\"\n" ); |
894 | fprintf (stream: file, format: "#include \"regs.h\"\n" ); |
895 | fprintf (stream: file, format: "#include \"tm-constrs.h\"\n" ); |
896 | fprintf (stream: file, format: "#include \"ggc.h\"\n" ); |
897 | fprintf (stream: file, format: "#include \"target.h\"\n\n" ); |
898 | } |
899 | |
900 | auto_vec<const char *, 10> output_files; |
901 | |
902 | static bool |
903 | handle_arg (const char *arg) |
904 | { |
905 | if (arg[1] == 'O') |
906 | { |
907 | output_files.safe_push (obj: &arg[2]); |
908 | return true; |
909 | } |
910 | return false; |
911 | } |
912 | |
913 | int |
914 | main (int argc, const char **argv) |
915 | { |
916 | progname = "genemit" ; |
917 | |
918 | if (!init_rtx_reader_args_cb (argc, argv, handle_arg)) |
919 | return (FATAL_EXIT_CODE); |
920 | |
921 | #define DEF_INTERNAL_OPTAB_FN(NAME, FLAGS, OPTAB, TYPE) \ |
922 | nofail_optabs[OPTAB##_optab] = true; |
923 | #include "internal-fn.def" |
924 | |
925 | /* Assign sequential codes to all entries in the machine description |
926 | in parallel with the tables in insn-output.cc. */ |
927 | |
928 | int npatterns = count_patterns (); |
929 | md_rtx_info info; |
930 | |
931 | bool to_stdout = false; |
932 | int npatterns_per_file = npatterns; |
933 | if (!output_files.is_empty ()) |
934 | npatterns_per_file = npatterns / output_files.length () + 1; |
935 | else |
936 | to_stdout = true; |
937 | |
938 | gcc_assert (npatterns_per_file > 1); |
939 | |
940 | /* Reverse so we can pop the first-added element. */ |
941 | output_files.reverse (); |
942 | |
943 | int count = 0; |
944 | FILE *file = NULL; |
945 | |
946 | /* Read the machine description. */ |
947 | while (read_md_rtx (&info)) |
948 | { |
949 | if (count == 0 || count == npatterns_per_file) |
950 | { |
951 | bool is_last = !to_stdout && output_files.is_empty (); |
952 | if (file && !is_last) |
953 | if (fclose (stream: file) != 0) |
954 | return FATAL_EXIT_CODE; |
955 | |
956 | if (!output_files.is_empty ()) |
957 | { |
958 | const char *const filename = output_files.pop (); |
959 | file = fopen (filename, "w" ); |
960 | } |
961 | else if (to_stdout) |
962 | file = stdout; |
963 | else |
964 | break; |
965 | |
966 | print_header (file); |
967 | count = 0; |
968 | } |
969 | |
970 | switch (GET_CODE (info.def)) |
971 | { |
972 | case DEFINE_INSN: |
973 | gen_insn (info: &info, file); |
974 | break; |
975 | |
976 | case DEFINE_EXPAND: |
977 | fprintf (stream: file, format: "/* %s:%d */\n" , info.loc.filename, info.loc.lineno); |
978 | gen_expand (info: &info, file); |
979 | break; |
980 | |
981 | case DEFINE_SPLIT: |
982 | fprintf (stream: file, format: "/* %s:%d */\n" , info.loc.filename, info.loc.lineno); |
983 | gen_split (info: &info, file); |
984 | break; |
985 | |
986 | case DEFINE_PEEPHOLE2: |
987 | fprintf (stream: file, format: "/* %s:%d */\n" , info.loc.filename, info.loc.lineno); |
988 | gen_split (info: &info, file); |
989 | break; |
990 | |
991 | default: |
992 | break; |
993 | } |
994 | |
995 | count++; |
996 | } |
997 | |
998 | /* Write out the routines to add CLOBBERs to a pattern and say whether they |
999 | clobber a hard reg. */ |
1000 | output_add_clobbers (info: &info, file); |
1001 | output_added_clobbers_hard_reg_p (file); |
1002 | |
1003 | for (overloaded_name *oname = rtx_reader_ptr->get_overloads (); |
1004 | oname; oname = oname->next) |
1005 | { |
1006 | handle_overloaded_code_for (oname, file); |
1007 | handle_overloaded_gen (oname, file); |
1008 | } |
1009 | |
1010 | return (fclose (stream: file) != 0 ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); |
1011 | } |
1012 | |