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