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