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