1/* Combine stack adjustments.
2 Copyright (C) 1987-2023 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 3, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14for more details.
15
16You should have received a copy of the GNU General Public License
17along 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
60struct csa_reflist
61{
62 HOST_WIDE_INT sp_offset;
63 rtx_insn *insn;
64 rtx *ref;
65 struct csa_reflist *next;
66};
67
68static bool stack_memref_p (rtx);
69static rtx single_set_for_csa (rtx_insn *);
70static void free_csa_reflist (struct csa_reflist *);
71static struct csa_reflist *record_one_stack_ref (rtx_insn *, rtx *,
72 struct csa_reflist *);
73static bool try_apply_stack_adjustment (rtx_insn *, struct csa_reflist *,
74 HOST_WIDE_INT, HOST_WIDE_INT,
75 bitmap, rtx_insn *);
76static void combine_stack_adjustments_for_block (basic_block, bitmap);
77
78
79/* Main entry point for stack adjustment combination. */
80
81static void
82combine_stack_adjustments (void)
83{
84 basic_block bb;
85 bitmap live = BITMAP_ALLOC (obstack: &reg_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
95static bool
96stack_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
115static rtx
116single_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
149static void
150free_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
164static struct csa_reflist *
165record_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
186static bool
187no_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
226static bool
227try_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
318static bool
319record_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
375static void
376maybe_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
399static void
400maybe_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
436static rtx_insn *
437prev_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
447static rtx_insn *
448next_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
461static void
462force_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
539static void
540combine_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: &reg_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: &reg_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
794static void
795rest_of_handle_stack_adjustments (void)
796{
797 df_note_add_problem ();
798 df_analyze ();
799 combine_stack_adjustments ();
800}
801
802namespace {
803
804const 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
817class pass_stack_adjustments : public rtl_opt_pass
818{
819public:
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
834bool
835pass_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
850rtl_opt_pass *
851make_pass_stack_adjustments (gcc::context *ctxt)
852{
853 return new pass_stack_adjustments (ctxt);
854}
855

source code of gcc/combine-stack-adj.cc