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