1 | /* Declarations for interface to insn recognizer and insn-output.cc. |
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 | #ifndef GCC_RECOG_H |
21 | #define GCC_RECOG_H |
22 | |
23 | /* For enum tree_code ERROR_MARK. */ |
24 | #include "tree.h" |
25 | |
26 | /* Random number that should be large enough for all purposes. Also define |
27 | a type that has at least MAX_RECOG_ALTERNATIVES + 1 bits, with the extra |
28 | bit giving an invalid value that can be used to mean "uninitialized". */ |
29 | #define MAX_RECOG_ALTERNATIVES 35 |
30 | typedef uint64_t alternative_mask; /* Keep in sync with genattrtab.cc. */ |
31 | |
32 | /* A mask of all alternatives. */ |
33 | #define ALL_ALTERNATIVES ((alternative_mask) -1) |
34 | |
35 | /* A mask containing just alternative X. */ |
36 | #define ALTERNATIVE_BIT(X) ((alternative_mask) 1 << (X)) |
37 | |
38 | /* Types of operands. */ |
39 | enum op_type { |
40 | OP_IN, |
41 | OP_OUT, |
42 | OP_INOUT |
43 | }; |
44 | |
45 | struct operand_alternative |
46 | { |
47 | /* Pointer to the beginning of the constraint string for this alternative, |
48 | for easier access by alternative number. */ |
49 | const char *constraint; |
50 | |
51 | /* The register class valid for this alternative (possibly NO_REGS). */ |
52 | ENUM_BITFIELD (reg_class) cl : 16; |
53 | |
54 | /* "Badness" of this alternative, computed from number of '?' and '!' |
55 | characters in the constraint string. */ |
56 | unsigned int reject : 16; |
57 | |
58 | /* -1 if no matching constraint was found, or an operand number. */ |
59 | int matches : 8; |
60 | /* The same information, but reversed: -1 if this operand is not |
61 | matched by any other, or the operand number of the operand that |
62 | matches this one. */ |
63 | int matched : 8; |
64 | |
65 | /* Nonzero if '&' was found in the constraint string. */ |
66 | unsigned int earlyclobber : 1; |
67 | /* Nonzero if TARGET_MEM_CONSTRAINT was found in the constraint |
68 | string. */ |
69 | unsigned int memory_ok : 1; |
70 | /* Nonzero if 'p' was found in the constraint string. */ |
71 | unsigned int is_address : 1; |
72 | /* Nonzero if 'X' was found in the constraint string, or if the constraint |
73 | string for this alternative was empty. */ |
74 | unsigned int anything_ok : 1; |
75 | |
76 | unsigned int unused : 12; |
77 | }; |
78 | |
79 | /* Return the class for operand I of alternative ALT, taking matching |
80 | constraints into account. */ |
81 | |
82 | inline enum reg_class |
83 | alternative_class (const operand_alternative *alt, int i) |
84 | { |
85 | return alt[i].matches >= 0 ? alt[alt[i].matches].cl : alt[i].cl; |
86 | } |
87 | |
88 | /* A class for substituting one rtx for another within an instruction, |
89 | or for recursively simplifying the instruction as-is. Derived classes |
90 | can record or filter certain decisions. */ |
91 | |
92 | class insn_propagation : public simplify_context |
93 | { |
94 | public: |
95 | /* Assignments for RESULT_FLAGS. |
96 | |
97 | UNSIMPLIFIED is true if a substitution has been made inside an rtx |
98 | X and if neither X nor its parent expressions could be simplified. |
99 | |
100 | FIRST_SPARE_RESULT is the first flag available for derived classes. */ |
101 | static const uint16_t UNSIMPLIFIED = 1U << 0; |
102 | static const uint16_t FIRST_SPARE_RESULT = 1U << 1; |
103 | |
104 | insn_propagation (rtx_insn *); |
105 | insn_propagation (rtx_insn *, rtx, rtx, bool = true); |
106 | bool apply_to_pattern (rtx *); |
107 | bool apply_to_rvalue (rtx *); |
108 | |
109 | /* Return true if we should accept a substitution into the address of |
110 | memory expression MEM. Undoing changes OLD_NUM_CHANGES and up restores |
111 | MEM's original address. */ |
112 | virtual bool check_mem (int /*old_num_changes*/, |
113 | rtx /*mem*/) { return true; } |
114 | |
115 | /* Note that we've simplified OLD_RTX into NEW_RTX. When substituting, |
116 | this only happens if a substitution occured within OLD_RTX. |
117 | Undoing OLD_NUM_CHANGES and up will restore the old form of OLD_RTX. |
118 | OLD_RESULT_FLAGS is the value that RESULT_FLAGS had before processing |
119 | OLD_RTX. */ |
120 | virtual void note_simplification (int /*old_num_changes*/, |
121 | uint16_t /*old_result_flags*/, |
122 | rtx /*old_rtx*/, rtx /*new_rtx*/) {} |
123 | |
124 | private: |
125 | bool apply_to_mem_1 (rtx); |
126 | bool apply_to_lvalue_1 (rtx); |
127 | bool apply_to_rvalue_1 (rtx *); |
128 | bool apply_to_pattern_1 (rtx *); |
129 | |
130 | public: |
131 | /* The instruction that we are simplifying or propagating into. */ |
132 | rtx_insn *insn; |
133 | |
134 | /* If FROM is nonnull, we're replacing FROM with TO, otherwise we're |
135 | just doing a recursive simplification. */ |
136 | rtx from; |
137 | rtx to; |
138 | |
139 | /* The number of times that we have replaced FROM with TO. */ |
140 | unsigned int num_replacements; |
141 | |
142 | /* A bitmask of flags that describe the result of the simplificiation; |
143 | see above for details. */ |
144 | uint16_t result_flags : 16; |
145 | |
146 | /* True if we should unshare TO when making the next substitution, |
147 | false if we can use TO itself. */ |
148 | uint16_t should_unshare : 1; |
149 | |
150 | /* True if we should call check_mem after substituting into a memory. */ |
151 | uint16_t should_check_mems : 1; |
152 | |
153 | /* True if we should call note_simplification after each simplification. */ |
154 | uint16_t should_note_simplifications : 1; |
155 | |
156 | /* For future expansion. */ |
157 | uint16_t spare : 13; |
158 | |
159 | /* Gives the reason that a substitution failed, for debug purposes. */ |
160 | const char *failure_reason; |
161 | }; |
162 | |
163 | /* Try to replace FROM with TO in INSN. SHARED_P is true if TO is shared |
164 | with other instructions, false if INSN can use TO directly. */ |
165 | |
166 | inline insn_propagation::insn_propagation (rtx_insn *insn, rtx from, rtx to, |
167 | bool shared_p) |
168 | : insn (insn), |
169 | from (from), |
170 | to (to), |
171 | num_replacements (0), |
172 | result_flags (0), |
173 | should_unshare (shared_p), |
174 | should_check_mems (false), |
175 | should_note_simplifications (false), |
176 | spare (0), |
177 | failure_reason (nullptr) |
178 | { |
179 | } |
180 | |
181 | /* Try to simplify INSN without performing a substitution. */ |
182 | |
183 | inline insn_propagation::insn_propagation (rtx_insn *insn) |
184 | : insn_propagation (insn, NULL_RTX, NULL_RTX) |
185 | { |
186 | } |
187 | |
188 | extern void init_recog (void); |
189 | extern void init_recog_no_volatile (void); |
190 | extern bool check_asm_operands (rtx); |
191 | extern int asm_operand_ok (rtx, const char *, const char **); |
192 | extern bool validate_change (rtx, rtx *, rtx, bool); |
193 | extern bool validate_unshare_change (rtx, rtx *, rtx, bool); |
194 | extern bool validate_change_xveclen (rtx, rtx *, int, bool); |
195 | extern bool canonicalize_change_group (rtx_insn *insn, rtx x); |
196 | extern bool insn_invalid_p (rtx_insn *, bool); |
197 | extern bool verify_changes (int); |
198 | extern void confirm_change_group (void); |
199 | extern bool apply_change_group (void); |
200 | extern int num_validated_changes (void); |
201 | extern void cancel_changes (int); |
202 | extern void temporarily_undo_changes (int); |
203 | extern void redo_changes (int); |
204 | extern bool constrain_operands (int, alternative_mask); |
205 | extern bool constrain_operands_cached (rtx_insn *, int); |
206 | extern bool memory_address_addr_space_p (machine_mode, rtx, addr_space_t, |
207 | code_helper = ERROR_MARK); |
208 | #define memory_address_p(mode,addr) \ |
209 | memory_address_addr_space_p ((mode), (addr), ADDR_SPACE_GENERIC) |
210 | extern bool strict_memory_address_addr_space_p (machine_mode, rtx, addr_space_t, |
211 | code_helper = ERROR_MARK); |
212 | #define strict_memory_address_p(mode,addr) \ |
213 | strict_memory_address_addr_space_p ((mode), (addr), ADDR_SPACE_GENERIC) |
214 | extern bool validate_replace_rtx_subexp (rtx, rtx, rtx_insn *, rtx *); |
215 | extern bool validate_replace_rtx (rtx, rtx, rtx_insn *); |
216 | extern bool validate_replace_rtx_part (rtx, rtx, rtx *, rtx_insn *); |
217 | extern bool validate_replace_rtx_part_nosimplify (rtx, rtx, rtx *, rtx_insn *); |
218 | extern void validate_replace_rtx_group (rtx, rtx, rtx_insn *); |
219 | extern void validate_replace_src_group (rtx, rtx, rtx_insn *); |
220 | extern bool validate_simplify_insn (rtx_insn *insn); |
221 | extern int num_changes_pending (void); |
222 | extern bool reg_fits_class_p (const_rtx, reg_class_t, int, machine_mode); |
223 | extern bool valid_insn_p (rtx_insn *); |
224 | |
225 | extern bool offsettable_memref_p (rtx); |
226 | extern bool offsettable_nonstrict_memref_p (rtx); |
227 | extern bool offsettable_address_addr_space_p (int, machine_mode, rtx, |
228 | addr_space_t); |
229 | #define offsettable_address_p(strict,mode,addr) \ |
230 | offsettable_address_addr_space_p ((strict), (mode), (addr), \ |
231 | ADDR_SPACE_GENERIC) |
232 | extern bool mode_dependent_address_p (rtx, addr_space_t); |
233 | |
234 | extern int recog (rtx, rtx_insn *, int *); |
235 | #ifndef GENERATOR_FILE |
236 | inline int recog_memoized (rtx_insn *insn); |
237 | #endif |
238 | extern void add_clobbers (rtx, int); |
239 | extern bool added_clobbers_hard_reg_p (int); |
240 | extern void (rtx_insn *); |
241 | extern void (rtx_insn *); |
242 | extern void (rtx_insn *insn); |
243 | extern void (rtx_insn *); |
244 | extern void (rtx_insn *); |
245 | extern void preprocess_constraints (int, int, const char **, |
246 | operand_alternative *, rtx **); |
247 | extern const operand_alternative *preprocess_insn_constraints (unsigned int); |
248 | extern void preprocess_constraints (rtx_insn *); |
249 | extern rtx_insn *peep2_next_insn (int); |
250 | extern bool peep2_regno_dead_p (int, int); |
251 | extern bool peep2_reg_dead_p (int, rtx); |
252 | #ifdef HARD_CONST |
253 | extern rtx peep2_find_free_register (int, int, const char *, |
254 | machine_mode, HARD_REG_SET *); |
255 | #endif |
256 | extern rtx_insn *peephole2_insns (rtx, rtx_insn *, int *); |
257 | |
258 | extern bool store_data_bypass_p (rtx_insn *, rtx_insn *); |
259 | extern bool if_test_bypass_p (rtx_insn *, rtx_insn *); |
260 | |
261 | extern void copy_frame_info_to_split_insn (rtx_insn *, rtx_insn *); |
262 | |
263 | #ifndef GENERATOR_FILE |
264 | /* Try recognizing the instruction INSN, |
265 | and return the code number that results. |
266 | Remember the code so that repeated calls do not |
267 | need to spend the time for actual rerecognition. |
268 | |
269 | This function is the normal interface to instruction recognition. |
270 | The automatically-generated function `recog' is normally called |
271 | through this one. */ |
272 | |
273 | inline int |
274 | recog_memoized (rtx_insn *insn) |
275 | { |
276 | if (INSN_CODE (insn) < 0) |
277 | INSN_CODE (insn) = recog (PATTERN (insn), insn, 0); |
278 | return INSN_CODE (insn); |
279 | } |
280 | #endif |
281 | |
282 | /* Skip chars until the next ',' or the end of the string. This is |
283 | useful to skip alternatives in a constraint string. */ |
284 | inline const char * |
285 | skip_alternative (const char *p) |
286 | { |
287 | const char *r = p; |
288 | while (*r != '\0' && *r != ',') |
289 | r++; |
290 | if (*r == ',') |
291 | r++; |
292 | return r; |
293 | } |
294 | |
295 | /* Nonzero means volatile operands are recognized. */ |
296 | extern int volatile_ok; |
297 | |
298 | /* RAII class for temporarily setting volatile_ok. */ |
299 | |
300 | class temporary_volatile_ok |
301 | { |
302 | public: |
303 | temporary_volatile_ok (int value) : save_volatile_ok (volatile_ok) |
304 | { |
305 | volatile_ok = value; |
306 | } |
307 | |
308 | ~temporary_volatile_ok () { volatile_ok = save_volatile_ok; } |
309 | |
310 | private: |
311 | temporary_volatile_ok (const temporary_volatile_ok &); |
312 | int save_volatile_ok; |
313 | }; |
314 | |
315 | /* Set by constrain_operands to the number of the alternative that |
316 | matched. */ |
317 | extern int which_alternative; |
318 | |
319 | /* The following vectors hold the results from insn_extract. */ |
320 | |
321 | struct recog_data_d |
322 | { |
323 | /* It is very tempting to make the 5 operand related arrays into a |
324 | structure and index on that. However, to be source compatible |
325 | with all of the existing md file insn constraints and output |
326 | templates, we need `operand' as a flat array. Without that |
327 | member, making an array for the rest seems pointless. */ |
328 | |
329 | /* Gives value of operand N. */ |
330 | rtx operand[MAX_RECOG_OPERANDS]; |
331 | |
332 | /* Gives location where operand N was found. */ |
333 | rtx *operand_loc[MAX_RECOG_OPERANDS]; |
334 | |
335 | /* Gives the constraint string for operand N. */ |
336 | const char *constraints[MAX_RECOG_OPERANDS]; |
337 | |
338 | /* Nonzero if operand N is a match_operator or a match_parallel. */ |
339 | char is_operator[MAX_RECOG_OPERANDS]; |
340 | |
341 | /* Gives the mode of operand N. */ |
342 | machine_mode operand_mode[MAX_RECOG_OPERANDS]; |
343 | |
344 | /* Gives the type (in, out, inout) for operand N. */ |
345 | enum op_type operand_type[MAX_RECOG_OPERANDS]; |
346 | |
347 | /* Gives location where the Nth duplicate-appearance of an operand |
348 | was found. This is something that matched MATCH_DUP. */ |
349 | rtx *dup_loc[MAX_DUP_OPERANDS]; |
350 | |
351 | /* Gives the operand number that was duplicated in the Nth |
352 | duplicate-appearance of an operand. */ |
353 | char dup_num[MAX_DUP_OPERANDS]; |
354 | |
355 | /* ??? Note that these are `char' instead of `unsigned char' to (try to) |
356 | avoid certain lossage from K&R C, wherein `unsigned char' default |
357 | promotes to `unsigned int' instead of `int' as in ISO C. As of 1999, |
358 | the most common places to bootstrap from K&R C are SunOS and HPUX, |
359 | both of which have signed characters by default. The only other |
360 | supported natives that have both K&R C and unsigned characters are |
361 | ROMP and Irix 3, and neither have been seen for a while, but do |
362 | continue to consider unsignedness when performing arithmetic inside |
363 | a comparison. */ |
364 | |
365 | /* The number of operands of the insn. */ |
366 | char n_operands; |
367 | |
368 | /* The number of MATCH_DUPs in the insn. */ |
369 | char n_dups; |
370 | |
371 | /* The number of alternatives in the constraints for the insn. */ |
372 | char n_alternatives; |
373 | |
374 | /* True if insn is ASM_OPERANDS. */ |
375 | bool is_asm; |
376 | |
377 | /* In case we are caching, hold insn data was generated for. */ |
378 | rtx_insn *insn; |
379 | }; |
380 | |
381 | extern struct recog_data_d recog_data; |
382 | |
383 | extern const operand_alternative *recog_op_alt; |
384 | |
385 | /* Return a pointer to an array in which index OP describes the constraints |
386 | on operand OP of the current instruction alternative (which_alternative). |
387 | Only valid after calling preprocess_constraints and constrain_operands. */ |
388 | |
389 | inline const operand_alternative * |
390 | which_op_alt () |
391 | { |
392 | gcc_checking_assert (IN_RANGE (which_alternative, 0, |
393 | recog_data.n_alternatives - 1)); |
394 | return &recog_op_alt[which_alternative * recog_data.n_operands]; |
395 | } |
396 | |
397 | /* A table defined in insn-output.cc that give information about |
398 | each insn-code value. */ |
399 | |
400 | typedef bool (*insn_operand_predicate_fn) (rtx, machine_mode); |
401 | typedef const char * (*insn_output_fn) (rtx *, rtx_insn *); |
402 | |
403 | struct insn_gen_fn |
404 | { |
405 | typedef void (*stored_funcptr) (void); |
406 | |
407 | template<typename ...Ts> |
408 | rtx_insn *operator() (Ts... args) const |
409 | { |
410 | typedef rtx_insn *(*funcptr) (decltype ((void) args, NULL_RTX)...); |
411 | return ((funcptr) func) (args...); |
412 | } |
413 | |
414 | // This is for compatibility of code that invokes functions like |
415 | // (*funcptr) (arg) |
416 | insn_gen_fn operator * (void) const { return *this; } |
417 | |
418 | // The wrapped function pointer must be public and there must not be any |
419 | // constructors. Otherwise the insn_data_d struct initializers generated |
420 | // by genoutput.cc will result in static initializer functions, which defeats |
421 | // the purpose of the generated insn_data_d array. |
422 | stored_funcptr func; |
423 | }; |
424 | |
425 | struct insn_operand_data |
426 | { |
427 | const insn_operand_predicate_fn predicate; |
428 | |
429 | const char *const constraint; |
430 | |
431 | ENUM_BITFIELD(machine_mode) const mode : 16; |
432 | |
433 | const char strict_low; |
434 | |
435 | const char is_operator; |
436 | |
437 | const char eliminable; |
438 | |
439 | const char allows_mem; |
440 | }; |
441 | |
442 | /* Legal values for insn_data.output_format. Indicate what type of data |
443 | is stored in insn_data.output. */ |
444 | #define INSN_OUTPUT_FORMAT_NONE 0 /* abort */ |
445 | #define INSN_OUTPUT_FORMAT_SINGLE 1 /* const char * */ |
446 | #define INSN_OUTPUT_FORMAT_MULTI 2 /* const char * const * */ |
447 | #define INSN_OUTPUT_FORMAT_FUNCTION 3 /* const char * (*)(...) */ |
448 | |
449 | struct insn_data_d |
450 | { |
451 | const char *const name; |
452 | #if HAVE_DESIGNATED_UNION_INITIALIZERS |
453 | union { |
454 | const char *single; |
455 | const char *const *multi; |
456 | insn_output_fn function; |
457 | } output; |
458 | #else |
459 | struct { |
460 | const char *single; |
461 | const char *const *multi; |
462 | insn_output_fn function; |
463 | } output; |
464 | #endif |
465 | const insn_gen_fn genfun; |
466 | const struct insn_operand_data *const operand; |
467 | |
468 | const char n_generator_args; |
469 | const char n_operands; |
470 | const char n_dups; |
471 | const char n_alternatives; |
472 | const char output_format; |
473 | }; |
474 | |
475 | extern const struct insn_data_d insn_data[]; |
476 | extern int peep2_current_count; |
477 | |
478 | #ifndef GENERATOR_FILE |
479 | #include "insn-codes.h" |
480 | |
481 | /* An enum of boolean attributes that may only depend on the current |
482 | subtarget, not on things like operands or compiler phase. */ |
483 | enum bool_attr { |
484 | BA_ENABLED, |
485 | BA_PREFERRED_FOR_SPEED, |
486 | BA_PREFERRED_FOR_SIZE, |
487 | BA_LAST = BA_PREFERRED_FOR_SIZE |
488 | }; |
489 | |
490 | /* Target-dependent globals. */ |
491 | struct target_recog { |
492 | bool x_initialized; |
493 | alternative_mask x_bool_attr_masks[NUM_INSN_CODES][BA_LAST + 1]; |
494 | operand_alternative *x_op_alt[NUM_INSN_CODES]; |
495 | }; |
496 | |
497 | extern struct target_recog default_target_recog; |
498 | #if SWITCHABLE_TARGET |
499 | extern struct target_recog *this_target_recog; |
500 | #else |
501 | #define this_target_recog (&default_target_recog) |
502 | #endif |
503 | |
504 | alternative_mask get_enabled_alternatives (rtx_insn *); |
505 | alternative_mask get_preferred_alternatives (rtx_insn *); |
506 | alternative_mask get_preferred_alternatives (rtx_insn *, basic_block); |
507 | bool check_bool_attrs (rtx_insn *); |
508 | |
509 | void recog_init (); |
510 | |
511 | /* This RAII class can help to undo tentative insn changes on failure. |
512 | When an object of the class goes out of scope, it undoes all group |
513 | changes that have been made via the validate_change machinery and |
514 | not yet confirmed via confirm_change_group. |
515 | |
516 | For example: |
517 | |
518 | insn_change_watermark watermark; |
519 | validate_change (..., true); // A |
520 | ... |
521 | if (test) |
522 | // Undoes change A. |
523 | return false; |
524 | ... |
525 | validate_change (..., true); // B |
526 | ... |
527 | if (test) |
528 | // Undoes changes A and B. |
529 | return false; |
530 | ... |
531 | confirm_change_group (); |
532 | |
533 | Code that wants to avoid this behavior can use keep (): |
534 | |
535 | insn_change_watermark watermark; |
536 | validate_change (..., true); // A |
537 | ... |
538 | if (test) |
539 | // Undoes change A. |
540 | return false; |
541 | ... |
542 | watermark.keep (); |
543 | validate_change (..., true); // B |
544 | ... |
545 | if (test) |
546 | // Undoes change B, but not A. |
547 | return false; |
548 | ... |
549 | confirm_change_group (); */ |
550 | class insn_change_watermark |
551 | { |
552 | public: |
553 | insn_change_watermark () : m_old_num_changes (num_validated_changes ()) {} |
554 | ~insn_change_watermark (); |
555 | void keep () { m_old_num_changes = num_validated_changes (); } |
556 | |
557 | private: |
558 | int m_old_num_changes; |
559 | }; |
560 | |
561 | inline insn_change_watermark::~insn_change_watermark () |
562 | { |
563 | if (m_old_num_changes < num_validated_changes ()) |
564 | cancel_changes (m_old_num_changes); |
565 | } |
566 | |
567 | #endif |
568 | |
569 | #endif /* GCC_RECOG_H */ |
570 | |