1 | /* Combine stack adjustments. |
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 | /* Track stack adjustments and stack memory references. Attempt to |
21 | reduce the number of stack adjustments by back-propagating across |
22 | the memory references. |
23 | |
24 | This is intended primarily for use with targets that do not define |
25 | ACCUMULATE_OUTGOING_ARGS. It is of significantly more value to |
26 | targets that define PREFERRED_STACK_BOUNDARY more aligned than |
27 | STACK_BOUNDARY (e.g. x86), or if not all registers can be pushed |
28 | (e.g. x86 fp regs) which would ordinarily have to be implemented |
29 | as a sub/mov pair due to restrictions in calls.cc. |
30 | |
31 | Propagation stops when any of the insns that need adjusting are |
32 | (a) no longer valid because we've exceeded their range, (b) a |
33 | non-trivial push instruction, or (c) a call instruction. |
34 | |
35 | Restriction B is based on the assumption that push instructions |
36 | are smaller or faster. If a port really wants to remove all |
37 | pushes, it should have defined ACCUMULATE_OUTGOING_ARGS. The |
38 | one exception that is made is for an add immediately followed |
39 | by a push. */ |
40 | |
41 | #include "config.h" |
42 | #include "system.h" |
43 | #include "coretypes.h" |
44 | #include "backend.h" |
45 | #include "rtl.h" |
46 | #include "df.h" |
47 | #include "insn-config.h" |
48 | #include "memmodel.h" |
49 | #include "emit-rtl.h" |
50 | #include "recog.h" |
51 | #include "cfgrtl.h" |
52 | #include "tree-pass.h" |
53 | #include "rtl-iter.h" |
54 | |
55 | |
56 | /* This structure records two kinds of stack references between stack |
57 | adjusting instructions: stack references in memory addresses for |
58 | regular insns and all stack references for debug insns. */ |
59 | |
60 | struct csa_reflist |
61 | { |
62 | HOST_WIDE_INT sp_offset; |
63 | rtx_insn *insn; |
64 | rtx *ref; |
65 | struct csa_reflist *next; |
66 | }; |
67 | |
68 | static bool stack_memref_p (rtx); |
69 | static rtx single_set_for_csa (rtx_insn *); |
70 | static void free_csa_reflist (struct csa_reflist *); |
71 | static struct csa_reflist *record_one_stack_ref (rtx_insn *, rtx *, |
72 | struct csa_reflist *); |
73 | static bool try_apply_stack_adjustment (rtx_insn *, struct csa_reflist *, |
74 | HOST_WIDE_INT, HOST_WIDE_INT, |
75 | bitmap, rtx_insn *); |
76 | static void combine_stack_adjustments_for_block (basic_block, bitmap); |
77 | |
78 | |
79 | /* Main entry point for stack adjustment combination. */ |
80 | |
81 | static void |
82 | combine_stack_adjustments (void) |
83 | { |
84 | basic_block bb; |
85 | bitmap live = BITMAP_ALLOC (obstack: ®_obstack); |
86 | |
87 | FOR_EACH_BB_FN (bb, cfun) |
88 | combine_stack_adjustments_for_block (bb, live); |
89 | |
90 | BITMAP_FREE (live); |
91 | } |
92 | |
93 | /* Recognize a MEM of the form (sp) or (plus sp const). */ |
94 | |
95 | static bool |
96 | stack_memref_p (rtx x) |
97 | { |
98 | if (!MEM_P (x)) |
99 | return false; |
100 | x = XEXP (x, 0); |
101 | |
102 | if (x == stack_pointer_rtx) |
103 | return true; |
104 | if (GET_CODE (x) == PLUS |
105 | && XEXP (x, 0) == stack_pointer_rtx |
106 | && CONST_INT_P (XEXP (x, 1))) |
107 | return true; |
108 | |
109 | return false; |
110 | } |
111 | |
112 | /* Recognize either normal single_set or the hack in i386.md for |
113 | tying fp and sp adjustments. */ |
114 | |
115 | static rtx |
116 | single_set_for_csa (rtx_insn *insn) |
117 | { |
118 | int i; |
119 | rtx tmp = single_set (insn); |
120 | if (tmp) |
121 | return tmp; |
122 | |
123 | if (!NONJUMP_INSN_P (insn) |
124 | || GET_CODE (PATTERN (insn)) != PARALLEL) |
125 | return NULL_RTX; |
126 | |
127 | tmp = PATTERN (insn); |
128 | if (GET_CODE (XVECEXP (tmp, 0, 0)) != SET) |
129 | return NULL_RTX; |
130 | |
131 | for (i = 1; i < XVECLEN (tmp, 0); ++i) |
132 | { |
133 | rtx this_rtx = XVECEXP (tmp, 0, i); |
134 | |
135 | /* The special case is allowing a no-op set. */ |
136 | if (GET_CODE (this_rtx) == SET |
137 | && SET_SRC (this_rtx) == SET_DEST (this_rtx)) |
138 | ; |
139 | else if (GET_CODE (this_rtx) != CLOBBER |
140 | && GET_CODE (this_rtx) != USE) |
141 | return NULL_RTX; |
142 | } |
143 | |
144 | return XVECEXP (tmp, 0, 0); |
145 | } |
146 | |
147 | /* Free the list of csa_reflist nodes. */ |
148 | |
149 | static void |
150 | free_csa_reflist (struct csa_reflist *reflist) |
151 | { |
152 | struct csa_reflist *next; |
153 | for (; reflist ; reflist = next) |
154 | { |
155 | next = reflist->next; |
156 | free (ptr: reflist); |
157 | } |
158 | } |
159 | |
160 | /* Create a new csa_reflist node from the given stack reference. |
161 | It is already known that the reference is either a MEM satisfying the |
162 | predicate stack_memref_p or a REG representing the stack pointer. */ |
163 | |
164 | static struct csa_reflist * |
165 | record_one_stack_ref (rtx_insn *insn, rtx *ref, struct csa_reflist *next_reflist) |
166 | { |
167 | struct csa_reflist *ml; |
168 | |
169 | ml = XNEW (struct csa_reflist); |
170 | |
171 | if (REG_P (*ref) || XEXP (*ref, 0) == stack_pointer_rtx) |
172 | ml->sp_offset = 0; |
173 | else |
174 | ml->sp_offset = INTVAL (XEXP (XEXP (*ref, 0), 1)); |
175 | |
176 | ml->insn = insn; |
177 | ml->ref = ref; |
178 | ml->next = next_reflist; |
179 | |
180 | return ml; |
181 | } |
182 | |
183 | /* We only know how to adjust the CFA; no other frame-related changes |
184 | may appear in any insn to be deleted. */ |
185 | |
186 | static bool |
187 | no_unhandled_cfa (rtx_insn *insn) |
188 | { |
189 | if (!RTX_FRAME_RELATED_P (insn)) |
190 | return true; |
191 | |
192 | /* No CFA notes at all is a legacy interpretation like |
193 | FRAME_RELATED_EXPR, and is context sensitive within |
194 | the prologue state machine. We can't handle that here. */ |
195 | bool has_cfa_adjust = false; |
196 | |
197 | for (rtx link = REG_NOTES (insn); link; link = XEXP (link, 1)) |
198 | switch (REG_NOTE_KIND (link)) |
199 | { |
200 | default: |
201 | break; |
202 | case REG_CFA_ADJUST_CFA: |
203 | has_cfa_adjust = true; |
204 | break; |
205 | |
206 | case REG_FRAME_RELATED_EXPR: |
207 | case REG_CFA_DEF_CFA: |
208 | case REG_CFA_OFFSET: |
209 | case REG_CFA_REGISTER: |
210 | case REG_CFA_EXPRESSION: |
211 | case REG_CFA_RESTORE: |
212 | case REG_CFA_SET_VDRAP: |
213 | case REG_CFA_WINDOW_SAVE: |
214 | case REG_CFA_FLUSH_QUEUE: |
215 | case REG_CFA_TOGGLE_RA_MANGLE: |
216 | return false; |
217 | } |
218 | |
219 | return has_cfa_adjust; |
220 | } |
221 | |
222 | /* Attempt to apply ADJUST to the stack adjusting insn INSN, as well |
223 | as each of the memories and stack references in REFLIST. Return true |
224 | on success. */ |
225 | |
226 | static bool |
227 | try_apply_stack_adjustment (rtx_insn *insn, struct csa_reflist *reflist, |
228 | HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta, |
229 | bitmap live, rtx_insn *other_insn) |
230 | { |
231 | struct csa_reflist *ml; |
232 | rtx set; |
233 | bool remove_equal = false; |
234 | |
235 | set = single_set_for_csa (insn); |
236 | if (MEM_P (SET_DEST (set))) |
237 | validate_change (insn, &SET_DEST (set), |
238 | replace_equiv_address (SET_DEST (set), stack_pointer_rtx), |
239 | 1); |
240 | else if (REG_P (SET_SRC (set))) |
241 | { |
242 | if (other_insn == NULL_RTX || live == NULL) |
243 | return false; |
244 | rtx other_set = single_set_for_csa (insn: other_insn); |
245 | if (SET_DEST (other_set) != stack_pointer_rtx |
246 | || GET_CODE (SET_SRC (other_set)) != PLUS |
247 | || XEXP (SET_SRC (other_set), 0) != stack_pointer_rtx |
248 | || !CONST_INT_P (XEXP (SET_SRC (other_set), 1))) |
249 | return false; |
250 | if (PATTERN (insn: other_insn) != other_set) |
251 | { |
252 | if (GET_CODE (PATTERN (other_insn)) != PARALLEL) |
253 | return false; |
254 | int i; |
255 | rtx p = PATTERN (insn: other_insn); |
256 | for (i = 0; i < XVECLEN (p, 0); ++i) |
257 | { |
258 | rtx this_rtx = XVECEXP (p, 0, i); |
259 | if (this_rtx == other_set) |
260 | continue; |
261 | if (GET_CODE (this_rtx) != CLOBBER) |
262 | return false; |
263 | if (!REG_P (XEXP (this_rtx, 0)) |
264 | || !HARD_REGISTER_P (XEXP (this_rtx, 0))) |
265 | return false; |
266 | unsigned int end_regno = END_REGNO (XEXP (this_rtx, 0)); |
267 | for (unsigned int regno = REGNO (XEXP (this_rtx, 0)); |
268 | regno < end_regno; ++regno) |
269 | if (bitmap_bit_p (live, regno)) |
270 | return false; |
271 | } |
272 | } |
273 | validate_change (insn, &PATTERN (insn), copy_rtx (PATTERN (insn: other_insn)), |
274 | 1); |
275 | set = single_set_for_csa (insn); |
276 | validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), |
277 | 1); |
278 | remove_equal = true; |
279 | } |
280 | else |
281 | validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1); |
282 | |
283 | for (ml = reflist; ml ; ml = ml->next) |
284 | { |
285 | rtx new_addr = plus_constant (Pmode, stack_pointer_rtx, |
286 | ml->sp_offset - delta); |
287 | rtx new_val; |
288 | |
289 | if (MEM_P (*ml->ref)) |
290 | new_val = replace_equiv_address_nv (*ml->ref, new_addr); |
291 | else if (GET_MODE (*ml->ref) == GET_MODE (stack_pointer_rtx)) |
292 | new_val = new_addr; |
293 | else |
294 | new_val = lowpart_subreg (GET_MODE (*ml->ref), op: new_addr, |
295 | GET_MODE (new_addr)); |
296 | validate_change (ml->insn, ml->ref, new_val, 1); |
297 | } |
298 | |
299 | if (apply_change_group ()) |
300 | { |
301 | /* Succeeded. Update our knowledge of the stack references. */ |
302 | for (ml = reflist; ml ; ml = ml->next) |
303 | ml->sp_offset -= delta; |
304 | |
305 | if (remove_equal) |
306 | remove_reg_equal_equiv_notes (insn); |
307 | return true; |
308 | } |
309 | else |
310 | return false; |
311 | } |
312 | |
313 | /* For non-debug insns, record all stack memory references in INSN |
314 | and return true if there were no other (unrecorded) references to the |
315 | stack pointer. For debug insns, record all stack references regardless |
316 | of context and unconditionally return true. */ |
317 | |
318 | static bool |
319 | record_stack_refs (rtx_insn *insn, struct csa_reflist **reflist) |
320 | { |
321 | subrtx_ptr_iterator::array_type array; |
322 | FOR_EACH_SUBRTX_PTR (iter, array, &PATTERN (insn), NONCONST) |
323 | { |
324 | rtx *loc = *iter; |
325 | rtx x = *loc; |
326 | switch (GET_CODE (x)) |
327 | { |
328 | case MEM: |
329 | if (!reg_mentioned_p (stack_pointer_rtx, x)) |
330 | iter.skip_subrtxes (); |
331 | /* We are not able to handle correctly all possible memrefs |
332 | containing stack pointer, so this check is necessary. */ |
333 | else if (stack_memref_p (x)) |
334 | { |
335 | *reflist = record_one_stack_ref (insn, ref: loc, next_reflist: *reflist); |
336 | iter.skip_subrtxes (); |
337 | } |
338 | /* Try harder for DEBUG_INSNs, handle e.g. |
339 | (mem (mem (sp + 16) + 4). */ |
340 | else if (!DEBUG_INSN_P (insn)) |
341 | return false; |
342 | break; |
343 | |
344 | case REG: |
345 | /* ??? We want be able to handle non-memory stack pointer |
346 | references later. For now just discard all insns referring to |
347 | stack pointer outside mem expressions. We would probably |
348 | want to teach validate_replace to simplify expressions first. |
349 | |
350 | We can't just compare with STACK_POINTER_RTX because the |
351 | reference to the stack pointer might be in some other mode. |
352 | In particular, an explicit clobber in an asm statement will |
353 | result in a QImode clobber. |
354 | |
355 | In DEBUG_INSNs, we want to replace all occurrences, otherwise |
356 | they will cause -fcompare-debug failures. */ |
357 | if (REGNO (x) == STACK_POINTER_REGNUM) |
358 | { |
359 | if (!DEBUG_INSN_P (insn)) |
360 | return false; |
361 | *reflist = record_one_stack_ref (insn, ref: loc, next_reflist: *reflist); |
362 | } |
363 | break; |
364 | |
365 | default: |
366 | break; |
367 | } |
368 | } |
369 | return true; |
370 | } |
371 | |
372 | /* If INSN has a REG_ARGS_SIZE note, move it to LAST. |
373 | AFTER is true iff LAST follows INSN in the instruction stream. */ |
374 | |
375 | static void |
376 | maybe_move_args_size_note (rtx_insn *last, rtx_insn *insn, bool after) |
377 | { |
378 | rtx note, last_note; |
379 | |
380 | note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX); |
381 | if (note == NULL) |
382 | return; |
383 | |
384 | last_note = find_reg_note (last, REG_ARGS_SIZE, NULL_RTX); |
385 | if (last_note) |
386 | { |
387 | /* The ARGS_SIZE notes are *not* cumulative. They represent an |
388 | absolute value, and the "most recent" note wins. */ |
389 | if (!after) |
390 | XEXP (last_note, 0) = XEXP (note, 0); |
391 | } |
392 | else |
393 | add_reg_note (last, REG_ARGS_SIZE, XEXP (note, 0)); |
394 | } |
395 | |
396 | /* Merge any REG_CFA_ADJUST_CFA note from SRC into DST. |
397 | AFTER is true iff DST follows SRC in the instruction stream. */ |
398 | |
399 | static void |
400 | maybe_merge_cfa_adjust (rtx_insn *dst, rtx_insn *src, bool after) |
401 | { |
402 | rtx snote = NULL, dnote = NULL; |
403 | rtx sexp, dexp; |
404 | rtx exp1, exp2; |
405 | |
406 | if (RTX_FRAME_RELATED_P (src)) |
407 | snote = find_reg_note (src, REG_CFA_ADJUST_CFA, NULL_RTX); |
408 | if (snote == NULL) |
409 | return; |
410 | sexp = XEXP (snote, 0); |
411 | |
412 | if (RTX_FRAME_RELATED_P (dst)) |
413 | dnote = find_reg_note (dst, REG_CFA_ADJUST_CFA, NULL_RTX); |
414 | if (dnote == NULL) |
415 | { |
416 | add_reg_note (dst, REG_CFA_ADJUST_CFA, sexp); |
417 | return; |
418 | } |
419 | dexp = XEXP (dnote, 0); |
420 | |
421 | gcc_assert (GET_CODE (sexp) == SET); |
422 | gcc_assert (GET_CODE (dexp) == SET); |
423 | |
424 | if (after) |
425 | exp1 = dexp, exp2 = sexp; |
426 | else |
427 | exp1 = sexp, exp2 = dexp; |
428 | |
429 | SET_SRC (exp1) = simplify_replace_rtx (SET_SRC (exp1), SET_DEST (exp2), |
430 | SET_SRC (exp2)); |
431 | XEXP (dnote, 0) = exp1; |
432 | } |
433 | |
434 | /* Return the next (or previous) active insn within BB. */ |
435 | |
436 | static rtx_insn * |
437 | prev_active_insn_bb (basic_block bb, rtx_insn *insn) |
438 | { |
439 | for (insn = PREV_INSN (insn); |
440 | insn != PREV_INSN (BB_HEAD (bb)); |
441 | insn = PREV_INSN (insn)) |
442 | if (active_insn_p (insn)) |
443 | return insn; |
444 | return NULL; |
445 | } |
446 | |
447 | static rtx_insn * |
448 | next_active_insn_bb (basic_block bb, rtx_insn *insn) |
449 | { |
450 | for (insn = NEXT_INSN (insn); |
451 | insn != NEXT_INSN (BB_END (bb)); |
452 | insn = NEXT_INSN (insn)) |
453 | if (active_insn_p (insn)) |
454 | return insn; |
455 | return NULL; |
456 | } |
457 | |
458 | /* If INSN has a REG_ARGS_SIZE note, if possible move it to PREV. Otherwise |
459 | search for a nearby candidate within BB where we can stick the note. */ |
460 | |
461 | static void |
462 | force_move_args_size_note (basic_block bb, rtx_insn *prev, rtx_insn *insn) |
463 | { |
464 | rtx note; |
465 | rtx_insn *test, *next_candidate, *prev_candidate; |
466 | |
467 | /* If PREV exists, tail-call to the logic in the other function. */ |
468 | if (prev) |
469 | { |
470 | maybe_move_args_size_note (last: prev, insn, after: false); |
471 | return; |
472 | } |
473 | |
474 | /* First, make sure there's anything that needs doing. */ |
475 | note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX); |
476 | if (note == NULL) |
477 | return; |
478 | |
479 | /* We need to find a spot between the previous and next exception points |
480 | where we can place the note and "properly" deallocate the arguments. */ |
481 | next_candidate = prev_candidate = NULL; |
482 | |
483 | /* It is often the case that we have insns in the order: |
484 | call |
485 | add sp (previous deallocation) |
486 | sub sp (align for next arglist) |
487 | push arg |
488 | and the add/sub cancel. Therefore we begin by searching forward. */ |
489 | |
490 | test = insn; |
491 | while ((test = next_active_insn_bb (bb, insn: test)) != NULL) |
492 | { |
493 | /* Found an existing note: nothing to do. */ |
494 | if (find_reg_note (test, REG_ARGS_SIZE, NULL_RTX)) |
495 | return; |
496 | /* Found something that affects unwinding. Stop searching. */ |
497 | if (CALL_P (test) || !insn_nothrow_p (test)) |
498 | break; |
499 | if (next_candidate == NULL) |
500 | next_candidate = test; |
501 | } |
502 | |
503 | test = insn; |
504 | while ((test = prev_active_insn_bb (bb, insn: test)) != NULL) |
505 | { |
506 | rtx tnote; |
507 | /* Found a place that seems logical to adjust the stack. */ |
508 | tnote = find_reg_note (test, REG_ARGS_SIZE, NULL_RTX); |
509 | if (tnote) |
510 | { |
511 | XEXP (tnote, 0) = XEXP (note, 0); |
512 | return; |
513 | } |
514 | if (prev_candidate == NULL) |
515 | prev_candidate = test; |
516 | /* Found something that affects unwinding. Stop searching. */ |
517 | if (CALL_P (test) || !insn_nothrow_p (test)) |
518 | break; |
519 | } |
520 | |
521 | if (prev_candidate) |
522 | test = prev_candidate; |
523 | else if (next_candidate) |
524 | test = next_candidate; |
525 | else |
526 | { |
527 | /* ??? We *must* have a place, lest we ICE on the lost adjustment. |
528 | Options are: dummy clobber insn, nop, or prevent the removal of |
529 | the sp += 0 insn. */ |
530 | /* TODO: Find another way to indicate to the dwarf2 code that we |
531 | have not in fact lost an adjustment. */ |
532 | test = emit_insn_before (gen_rtx_CLOBBER (VOIDmode, const0_rtx), insn); |
533 | } |
534 | add_reg_note (test, REG_ARGS_SIZE, XEXP (note, 0)); |
535 | } |
536 | |
537 | /* Subroutine of combine_stack_adjustments, called for each basic block. */ |
538 | |
539 | static void |
540 | combine_stack_adjustments_for_block (basic_block bb, bitmap live) |
541 | { |
542 | HOST_WIDE_INT last_sp_adjust = 0; |
543 | rtx_insn *last_sp_set = NULL; |
544 | rtx_insn *last2_sp_set = NULL; |
545 | bitmap last_sp_live = NULL; |
546 | struct csa_reflist *reflist = NULL; |
547 | bitmap copy = NULL; |
548 | rtx_insn *insn, *next; |
549 | rtx set; |
550 | bool end_of_block = false; |
551 | |
552 | bitmap_copy (live, DF_LR_IN (bb)); |
553 | df_simulate_initialize_forwards (bb, live); |
554 | |
555 | for (insn = BB_HEAD (bb); !end_of_block ; insn = next) |
556 | { |
557 | end_of_block = insn == BB_END (bb); |
558 | next = NEXT_INSN (insn); |
559 | |
560 | if (! INSN_P (insn)) |
561 | continue; |
562 | |
563 | set = single_set_for_csa (insn); |
564 | if (set && find_reg_note (insn, REG_STACK_CHECK, NULL_RTX)) |
565 | set = NULL_RTX; |
566 | if (set) |
567 | { |
568 | rtx dest = SET_DEST (set); |
569 | rtx src = SET_SRC (set); |
570 | HOST_WIDE_INT this_adjust = 0; |
571 | |
572 | /* Find constant additions to the stack pointer. */ |
573 | if (dest == stack_pointer_rtx |
574 | && GET_CODE (src) == PLUS |
575 | && XEXP (src, 0) == stack_pointer_rtx |
576 | && CONST_INT_P (XEXP (src, 1))) |
577 | this_adjust = INTVAL (XEXP (src, 1)); |
578 | /* Or such additions turned by postreload into a store of |
579 | equivalent register. */ |
580 | else if (dest == stack_pointer_rtx |
581 | && REG_P (src) |
582 | && REGNO (src) != STACK_POINTER_REGNUM) |
583 | if (rtx equal = find_reg_note (insn, REG_EQUAL, NULL_RTX)) |
584 | if (GET_CODE (XEXP (equal, 0)) == PLUS |
585 | && XEXP (XEXP (equal, 0), 0) == stack_pointer_rtx |
586 | && CONST_INT_P (XEXP (XEXP (equal, 0), 1))) |
587 | this_adjust = INTVAL (XEXP (XEXP (equal, 0), 1)); |
588 | |
589 | if (this_adjust) |
590 | { |
591 | /* If we've not seen an adjustment previously, record |
592 | it now and continue. */ |
593 | if (! last_sp_set) |
594 | { |
595 | last_sp_set = insn; |
596 | last_sp_adjust = this_adjust; |
597 | if (REG_P (src)) |
598 | { |
599 | if (copy == NULL) |
600 | copy = BITMAP_ALLOC (obstack: ®_obstack); |
601 | last_sp_live = copy; |
602 | bitmap_copy (last_sp_live, live); |
603 | } |
604 | else |
605 | last_sp_live = NULL; |
606 | df_simulate_one_insn_forwards (bb, insn, live); |
607 | continue; |
608 | } |
609 | |
610 | /* If not all recorded refs can be adjusted, or the |
611 | adjustment is now too large for a constant addition, |
612 | we cannot merge the two stack adjustments. |
613 | |
614 | Also we need to be careful to not move stack pointer |
615 | such that we create stack accesses outside the allocated |
616 | area. We can combine an allocation into the first insn, |
617 | or a deallocation into the second insn. We cannot |
618 | combine an allocation followed by a deallocation. |
619 | |
620 | The only somewhat frequent occurrence of the later is when |
621 | a function allocates a stack frame but does not use it. |
622 | For this case, we would need to analyze rtl stream to be |
623 | sure that allocated area is really unused. This means not |
624 | only checking the memory references, but also all registers |
625 | or global memory references possibly containing a stack |
626 | frame address. |
627 | |
628 | Perhaps the best way to address this problem is to teach |
629 | gcc not to allocate stack for objects never used. */ |
630 | |
631 | /* Combine an allocation into the first instruction. */ |
632 | if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0) |
633 | { |
634 | if (no_unhandled_cfa (insn) |
635 | && try_apply_stack_adjustment (insn: last_sp_set, reflist, |
636 | new_adjust: last_sp_adjust |
637 | + this_adjust, |
638 | delta: this_adjust, |
639 | live: last_sp_live, |
640 | other_insn: insn)) |
641 | { |
642 | /* It worked! */ |
643 | maybe_move_args_size_note (last: last_sp_set, insn, after: false); |
644 | maybe_merge_cfa_adjust (dst: last_sp_set, src: insn, after: false); |
645 | delete_insn (insn); |
646 | last_sp_adjust += this_adjust; |
647 | last_sp_live = NULL; |
648 | continue; |
649 | } |
650 | } |
651 | |
652 | /* Otherwise we have a deallocation. Do not combine with |
653 | a previous allocation. Combine into the second insn. */ |
654 | else if (STACK_GROWS_DOWNWARD |
655 | ? last_sp_adjust >= 0 : last_sp_adjust <= 0) |
656 | { |
657 | if (no_unhandled_cfa (insn: last_sp_set) |
658 | && !REG_P (src) |
659 | && try_apply_stack_adjustment (insn, reflist, |
660 | new_adjust: last_sp_adjust |
661 | + this_adjust, |
662 | delta: -last_sp_adjust, |
663 | NULL, NULL)) |
664 | { |
665 | /* It worked! */ |
666 | maybe_move_args_size_note (last: insn, insn: last_sp_set, after: true); |
667 | maybe_merge_cfa_adjust (dst: insn, src: last_sp_set, after: true); |
668 | delete_insn (last_sp_set); |
669 | last_sp_set = insn; |
670 | last_sp_adjust += this_adjust; |
671 | last_sp_live = NULL; |
672 | free_csa_reflist (reflist); |
673 | reflist = NULL; |
674 | df_simulate_one_insn_forwards (bb, insn, live); |
675 | continue; |
676 | } |
677 | } |
678 | |
679 | /* Combination failed. Restart processing from here. If |
680 | deallocation+allocation conspired to cancel, we can |
681 | delete the old deallocation insn. */ |
682 | if (last_sp_set) |
683 | { |
684 | if (last_sp_adjust == 0 && no_unhandled_cfa (insn: last_sp_set)) |
685 | { |
686 | maybe_move_args_size_note (last: insn, insn: last_sp_set, after: true); |
687 | maybe_merge_cfa_adjust (dst: insn, src: last_sp_set, after: true); |
688 | delete_insn (last_sp_set); |
689 | } |
690 | else |
691 | last2_sp_set = last_sp_set; |
692 | } |
693 | free_csa_reflist (reflist); |
694 | reflist = NULL; |
695 | last_sp_set = insn; |
696 | last_sp_adjust = this_adjust; |
697 | if (REG_P (src)) |
698 | { |
699 | if (copy == NULL) |
700 | copy = BITMAP_ALLOC (obstack: ®_obstack); |
701 | last_sp_live = copy; |
702 | bitmap_copy (last_sp_live, live); |
703 | } |
704 | else |
705 | last_sp_live = NULL; |
706 | df_simulate_one_insn_forwards (bb, insn, live); |
707 | continue; |
708 | } |
709 | |
710 | /* Find a store with pre-(dec|inc)rement or pre-modify of exactly |
711 | the previous adjustment and turn it into a simple store. This |
712 | is equivalent to anticipating the stack adjustment so this must |
713 | be an allocation. */ |
714 | if (MEM_P (dest) |
715 | && ((STACK_GROWS_DOWNWARD |
716 | ? (GET_CODE (XEXP (dest, 0)) == PRE_DEC |
717 | && known_eq (last_sp_adjust, |
718 | GET_MODE_SIZE (GET_MODE (dest)))) |
719 | : (GET_CODE (XEXP (dest, 0)) == PRE_INC |
720 | && known_eq (-last_sp_adjust, |
721 | GET_MODE_SIZE (GET_MODE (dest))))) |
722 | || ((STACK_GROWS_DOWNWARD |
723 | ? last_sp_adjust >= 0 : last_sp_adjust <= 0) |
724 | && GET_CODE (XEXP (dest, 0)) == PRE_MODIFY |
725 | && GET_CODE (XEXP (XEXP (dest, 0), 1)) == PLUS |
726 | && XEXP (XEXP (XEXP (dest, 0), 1), 0) |
727 | == stack_pointer_rtx |
728 | && GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) |
729 | == CONST_INT |
730 | && INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)) |
731 | == -last_sp_adjust)) |
732 | && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx |
733 | && !reg_mentioned_p (stack_pointer_rtx, src) |
734 | && memory_address_p (GET_MODE (dest), stack_pointer_rtx) |
735 | && try_apply_stack_adjustment (insn, reflist, new_adjust: 0, |
736 | delta: -last_sp_adjust, |
737 | NULL, NULL)) |
738 | { |
739 | if (last2_sp_set) |
740 | maybe_move_args_size_note (last: last2_sp_set, insn: last_sp_set, after: false); |
741 | else |
742 | maybe_move_args_size_note (last: insn, insn: last_sp_set, after: true); |
743 | delete_insn (last_sp_set); |
744 | free_csa_reflist (reflist); |
745 | reflist = NULL; |
746 | last_sp_set = NULL; |
747 | last_sp_adjust = 0; |
748 | last_sp_live = NULL; |
749 | df_simulate_one_insn_forwards (bb, insn, live); |
750 | continue; |
751 | } |
752 | } |
753 | |
754 | if (!CALL_P (insn) && last_sp_set && record_stack_refs (insn, reflist: &reflist)) |
755 | { |
756 | df_simulate_one_insn_forwards (bb, insn, live); |
757 | continue; |
758 | } |
759 | |
760 | /* Otherwise, we were not able to process the instruction. |
761 | Do not continue collecting data across such a one. */ |
762 | if (last_sp_set |
763 | && (CALL_P (insn) |
764 | || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))) |
765 | { |
766 | if (last_sp_set && last_sp_adjust == 0) |
767 | { |
768 | force_move_args_size_note (bb, prev: last2_sp_set, insn: last_sp_set); |
769 | delete_insn (last_sp_set); |
770 | } |
771 | free_csa_reflist (reflist); |
772 | reflist = NULL; |
773 | last2_sp_set = NULL; |
774 | last_sp_set = NULL; |
775 | last_sp_adjust = 0; |
776 | last_sp_live = NULL; |
777 | } |
778 | |
779 | df_simulate_one_insn_forwards (bb, insn, live); |
780 | } |
781 | |
782 | if (last_sp_set && last_sp_adjust == 0) |
783 | { |
784 | force_move_args_size_note (bb, prev: last2_sp_set, insn: last_sp_set); |
785 | delete_insn (last_sp_set); |
786 | } |
787 | |
788 | if (reflist) |
789 | free_csa_reflist (reflist); |
790 | if (copy) |
791 | BITMAP_FREE (copy); |
792 | } |
793 | |
794 | static void |
795 | rest_of_handle_stack_adjustments (void) |
796 | { |
797 | df_note_add_problem (); |
798 | df_analyze (); |
799 | combine_stack_adjustments (); |
800 | } |
801 | |
802 | namespace { |
803 | |
804 | const pass_data pass_data_stack_adjustments = |
805 | { |
806 | .type: RTL_PASS, /* type */ |
807 | .name: "csa" , /* name */ |
808 | .optinfo_flags: OPTGROUP_NONE, /* optinfo_flags */ |
809 | .tv_id: TV_COMBINE_STACK_ADJUST, /* tv_id */ |
810 | .properties_required: 0, /* properties_required */ |
811 | .properties_provided: 0, /* properties_provided */ |
812 | .properties_destroyed: 0, /* properties_destroyed */ |
813 | .todo_flags_start: 0, /* todo_flags_start */ |
814 | TODO_df_finish, /* todo_flags_finish */ |
815 | }; |
816 | |
817 | class pass_stack_adjustments : public rtl_opt_pass |
818 | { |
819 | public: |
820 | pass_stack_adjustments (gcc::context *ctxt) |
821 | : rtl_opt_pass (pass_data_stack_adjustments, ctxt) |
822 | {} |
823 | |
824 | /* opt_pass methods: */ |
825 | bool gate (function *) final override; |
826 | unsigned int execute (function *) final override |
827 | { |
828 | rest_of_handle_stack_adjustments (); |
829 | return 0; |
830 | } |
831 | |
832 | }; // class pass_stack_adjustments |
833 | |
834 | bool |
835 | pass_stack_adjustments::gate (function *) |
836 | { |
837 | /* This is kind of a heuristic. We need to run combine_stack_adjustments |
838 | even for machines with possibly nonzero TARGET_RETURN_POPS_ARGS |
839 | and ACCUMULATE_OUTGOING_ARGS. We expect that only ports having |
840 | push instructions will have popping returns. */ |
841 | #ifndef PUSH_ROUNDING |
842 | if (ACCUMULATE_OUTGOING_ARGS) |
843 | return false; |
844 | #endif |
845 | return flag_combine_stack_adjustments; |
846 | } |
847 | |
848 | } // anon namespace |
849 | |
850 | rtl_opt_pass * |
851 | make_pass_stack_adjustments (gcc::context *ctxt) |
852 | { |
853 | return new pass_stack_adjustments (ctxt); |
854 | } |
855 | |