Mercurial > hg > CbC > CbC_gcc
diff gcc/combine-stack-adj.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | f6334be47118 |
children | 84e7813d76e9 |
line wrap: on
line diff
--- a/gcc/combine-stack-adj.c Sun Aug 21 07:07:55 2011 +0900 +++ b/gcc/combine-stack-adj.c Fri Oct 27 22:46:09 2017 +0900 @@ -1,7 +1,5 @@ /* Combine stack adjustments. - Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, - 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010 Free Software Foundation, Inc. + Copyright (C) 1987-2017 Free Software Foundation, Inc. This file is part of GCC. @@ -43,33 +41,18 @@ #include "config.h" #include "system.h" #include "coretypes.h" -#include "tm.h" +#include "backend.h" #include "rtl.h" -#include "tm_p.h" +#include "df.h" #include "insn-config.h" +#include "memmodel.h" +#include "emit-rtl.h" #include "recog.h" -#include "output.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "function.h" -#include "expr.h" -#include "basic-block.h" -#include "df.h" -#include "except.h" -#include "reload.h" -#include "timevar.h" +#include "cfgrtl.h" #include "tree-pass.h" +#include "rtl-iter.h" -/* Turn STACK_GROWS_DOWNWARD into a boolean. */ -#ifdef STACK_GROWS_DOWNWARD -#undef STACK_GROWS_DOWNWARD -#define STACK_GROWS_DOWNWARD 1 -#else -#define STACK_GROWS_DOWNWARD 0 -#endif - /* This structure records two kinds of stack references between stack adjusting instructions: stack references in memory addresses for regular insns and all stack references for debug insns. */ @@ -77,19 +60,19 @@ struct csa_reflist { HOST_WIDE_INT sp_offset; - rtx insn, *ref; + rtx_insn *insn; + rtx *ref; struct csa_reflist *next; }; static int stack_memref_p (rtx); -static rtx single_set_for_csa (rtx); +static rtx single_set_for_csa (rtx_insn *); static void free_csa_reflist (struct csa_reflist *); -static struct csa_reflist *record_one_stack_ref (rtx, rtx *, +static struct csa_reflist *record_one_stack_ref (rtx_insn *, rtx *, struct csa_reflist *); -static int try_apply_stack_adjustment (rtx, struct csa_reflist *, +static int try_apply_stack_adjustment (rtx_insn *, struct csa_reflist *, HOST_WIDE_INT, HOST_WIDE_INT); static void combine_stack_adjustments_for_block (basic_block); -static int record_stack_refs (rtx *, void *); /* Main entry point for stack adjustment combination. */ @@ -99,7 +82,7 @@ { basic_block bb; - FOR_EACH_BB (bb) + FOR_EACH_BB_FN (bb, cfun) combine_stack_adjustments_for_block (bb); } @@ -126,7 +109,7 @@ tying fp and sp adjustments. */ static rtx -single_set_for_csa (rtx insn) +single_set_for_csa (rtx_insn *insn) { int i; rtx tmp = single_set (insn); @@ -175,7 +158,7 @@ predicate stack_memref_p or a REG representing the stack pointer. */ static struct csa_reflist * -record_one_stack_ref (rtx insn, rtx *ref, struct csa_reflist *next_reflist) +record_one_stack_ref (rtx_insn *insn, rtx *ref, struct csa_reflist *next_reflist) { struct csa_reflist *ml; @@ -193,12 +176,51 @@ return ml; } +/* We only know how to adjust the CFA; no other frame-related changes + may appear in any insn to be deleted. */ + +static bool +no_unhandled_cfa (rtx_insn *insn) +{ + if (!RTX_FRAME_RELATED_P (insn)) + return true; + + /* No CFA notes at all is a legacy interpretation like + FRAME_RELATED_EXPR, and is context sensitive within + the prologue state machine. We can't handle that here. */ + bool has_cfa_adjust = false; + + for (rtx link = REG_NOTES (insn); link; link = XEXP (link, 1)) + switch (REG_NOTE_KIND (link)) + { + default: + break; + case REG_CFA_ADJUST_CFA: + has_cfa_adjust = true; + break; + + case REG_FRAME_RELATED_EXPR: + case REG_CFA_DEF_CFA: + case REG_CFA_OFFSET: + case REG_CFA_REGISTER: + case REG_CFA_EXPRESSION: + case REG_CFA_RESTORE: + case REG_CFA_SET_VDRAP: + case REG_CFA_WINDOW_SAVE: + case REG_CFA_FLUSH_QUEUE: + case REG_CFA_TOGGLE_RA_MANGLE: + return false; + } + + return has_cfa_adjust; +} + /* Attempt to apply ADJUST to the stack adjusting insn INSN, as well as each of the memories and stack references in REFLIST. Return true on success. */ static int -try_apply_stack_adjustment (rtx insn, struct csa_reflist *reflist, +try_apply_stack_adjustment (rtx_insn *insn, struct csa_reflist *reflist, HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta) { struct csa_reflist *ml; @@ -214,7 +236,8 @@ for (ml = reflist; ml ; ml = ml->next) { - rtx new_addr = plus_constant (stack_pointer_rtx, ml->sp_offset - delta); + rtx new_addr = plus_constant (Pmode, stack_pointer_rtx, + ml->sp_offset - delta); rtx new_val; if (MEM_P (*ml->ref)) @@ -239,125 +262,228 @@ return 0; } -/* Called via for_each_rtx and used to record all stack memory and other - references in the insn and discard all other stack pointer references. */ -struct record_stack_refs_data -{ - rtx insn; - struct csa_reflist *reflist; -}; +/* For non-debug insns, record all stack memory references in INSN + and return true if there were no other (unrecorded) references to the + stack pointer. For debug insns, record all stack references regardless + of context and unconditionally return true. */ -static int -record_stack_refs (rtx *xp, void *data) +static bool +record_stack_refs (rtx_insn *insn, struct csa_reflist **reflist) { - rtx x = *xp; - struct record_stack_refs_data *d = - (struct record_stack_refs_data *) data; - if (!x) - return 0; - switch (GET_CODE (x)) + subrtx_ptr_iterator::array_type array; + FOR_EACH_SUBRTX_PTR (iter, array, &PATTERN (insn), NONCONST) { - case MEM: - if (!reg_mentioned_p (stack_pointer_rtx, x)) - return -1; - /* We are not able to handle correctly all possible memrefs containing - stack pointer, so this check is necessary. */ - if (stack_memref_p (x)) + rtx *loc = *iter; + rtx x = *loc; + switch (GET_CODE (x)) { - d->reflist = record_one_stack_ref (d->insn, xp, d->reflist); - return -1; + case MEM: + if (!reg_mentioned_p (stack_pointer_rtx, x)) + iter.skip_subrtxes (); + /* We are not able to handle correctly all possible memrefs + containing stack pointer, so this check is necessary. */ + else if (stack_memref_p (x)) + { + *reflist = record_one_stack_ref (insn, loc, *reflist); + iter.skip_subrtxes (); + } + /* Try harder for DEBUG_INSNs, handle e.g. + (mem (mem (sp + 16) + 4). */ + else if (!DEBUG_INSN_P (insn)) + return false; + break; + + case REG: + /* ??? We want be able to handle non-memory stack pointer + references later. For now just discard all insns referring to + stack pointer outside mem expressions. We would probably + want to teach validate_replace to simplify expressions first. + + We can't just compare with STACK_POINTER_RTX because the + reference to the stack pointer might be in some other mode. + In particular, an explicit clobber in an asm statement will + result in a QImode clobber. + + In DEBUG_INSNs, we want to replace all occurrences, otherwise + they will cause -fcompare-debug failures. */ + if (REGNO (x) == STACK_POINTER_REGNUM) + { + if (!DEBUG_INSN_P (insn)) + return false; + *reflist = record_one_stack_ref (insn, loc, *reflist); + } + break; + + default: + break; } - /* Try harder for DEBUG_INSNs, handle e.g. (mem (mem (sp + 16) + 4). */ - return !DEBUG_INSN_P (d->insn); - case REG: - /* ??? We want be able to handle non-memory stack pointer - references later. For now just discard all insns referring to - stack pointer outside mem expressions. We would probably - want to teach validate_replace to simplify expressions first. + } + return true; +} - We can't just compare with STACK_POINTER_RTX because the - reference to the stack pointer might be in some other mode. - In particular, an explicit clobber in an asm statement will - result in a QImode clobber. +/* If INSN has a REG_ARGS_SIZE note, move it to LAST. + AFTER is true iff LAST follows INSN in the instruction stream. */ + +static void +maybe_move_args_size_note (rtx_insn *last, rtx_insn *insn, bool after) +{ + rtx note, last_note; - In DEBUG_INSNs, we want to replace all occurrences, otherwise - they will cause -fcompare-debug failures. */ - if (REGNO (x) == STACK_POINTER_REGNUM) - { - if (!DEBUG_INSN_P (d->insn)) - return 1; - d->reflist = record_one_stack_ref (d->insn, xp, d->reflist); - return -1; - } - break; - default: - break; + note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX); + if (note == NULL) + return; + + last_note = find_reg_note (last, REG_ARGS_SIZE, NULL_RTX); + if (last_note) + { + /* The ARGS_SIZE notes are *not* cumulative. They represent an + absolute value, and the "most recent" note wins. */ + if (!after) + XEXP (last_note, 0) = XEXP (note, 0); } - return 0; + else + add_reg_note (last, REG_ARGS_SIZE, XEXP (note, 0)); } -/* Adjust or create REG_FRAME_RELATED_EXPR note when merging a stack - adjustment into a frame related insn. */ +/* Merge any REG_CFA_ADJUST_CFA note from SRC into DST. + AFTER is true iff DST follows SRC in the instruction stream. */ static void -adjust_frame_related_expr (rtx last_sp_set, rtx insn, - HOST_WIDE_INT this_adjust) +maybe_merge_cfa_adjust (rtx_insn *dst, rtx_insn *src, bool after) +{ + rtx snote = NULL, dnote = NULL; + rtx sexp, dexp; + rtx exp1, exp2; + + if (RTX_FRAME_RELATED_P (src)) + snote = find_reg_note (src, REG_CFA_ADJUST_CFA, NULL_RTX); + if (snote == NULL) + return; + sexp = XEXP (snote, 0); + + if (RTX_FRAME_RELATED_P (dst)) + dnote = find_reg_note (dst, REG_CFA_ADJUST_CFA, NULL_RTX); + if (dnote == NULL) + { + add_reg_note (dst, REG_CFA_ADJUST_CFA, sexp); + return; + } + dexp = XEXP (dnote, 0); + + gcc_assert (GET_CODE (sexp) == SET); + gcc_assert (GET_CODE (dexp) == SET); + + if (after) + exp1 = dexp, exp2 = sexp; + else + exp1 = sexp, exp2 = dexp; + + SET_SRC (exp1) = simplify_replace_rtx (SET_SRC (exp1), SET_DEST (exp2), + SET_SRC (exp2)); + XEXP (dnote, 0) = exp1; +} + +/* Return the next (or previous) active insn within BB. */ + +static rtx_insn * +prev_active_insn_bb (basic_block bb, rtx_insn *insn) { - rtx note = find_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, NULL_RTX); - rtx new_expr = NULL_RTX; + for (insn = PREV_INSN (insn); + insn != PREV_INSN (BB_HEAD (bb)); + insn = PREV_INSN (insn)) + if (active_insn_p (insn)) + return insn; + return NULL; +} + +static rtx_insn * +next_active_insn_bb (basic_block bb, rtx_insn *insn) +{ + for (insn = NEXT_INSN (insn); + insn != NEXT_INSN (BB_END (bb)); + insn = NEXT_INSN (insn)) + if (active_insn_p (insn)) + return insn; + return NULL; +} - if (note == NULL_RTX && RTX_FRAME_RELATED_P (insn)) +/* If INSN has a REG_ARGS_SIZE note, if possible move it to PREV. Otherwise + search for a nearby candidate within BB where we can stick the note. */ + +static void +force_move_args_size_note (basic_block bb, rtx_insn *prev, rtx_insn *insn) +{ + rtx note; + rtx_insn *test, *next_candidate, *prev_candidate; + + /* If PREV exists, tail-call to the logic in the other function. */ + if (prev) + { + maybe_move_args_size_note (prev, insn, false); + return; + } + + /* First, make sure there's anything that needs doing. */ + note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX); + if (note == NULL) return; - if (note - && GET_CODE (XEXP (note, 0)) == SEQUENCE - && XVECLEN (XEXP (note, 0), 0) >= 2) - { - rtx expr = XEXP (note, 0); - rtx last = XVECEXP (expr, 0, XVECLEN (expr, 0) - 1); - int i; + /* We need to find a spot between the previous and next exception points + where we can place the note and "properly" deallocate the arguments. */ + next_candidate = prev_candidate = NULL; + + /* It is often the case that we have insns in the order: + call + add sp (previous deallocation) + sub sp (align for next arglist) + push arg + and the add/sub cancel. Therefore we begin by searching forward. */ - if (GET_CODE (last) == SET - && RTX_FRAME_RELATED_P (last) == RTX_FRAME_RELATED_P (insn) - && SET_DEST (last) == stack_pointer_rtx - && GET_CODE (SET_SRC (last)) == PLUS - && XEXP (SET_SRC (last), 0) == stack_pointer_rtx - && CONST_INT_P (XEXP (SET_SRC (last), 1))) + test = insn; + while ((test = next_active_insn_bb (bb, test)) != NULL) + { + /* Found an existing note: nothing to do. */ + if (find_reg_note (test, REG_ARGS_SIZE, NULL_RTX)) + return; + /* Found something that affects unwinding. Stop searching. */ + if (CALL_P (test) || !insn_nothrow_p (test)) + break; + if (next_candidate == NULL) + next_candidate = test; + } + + test = insn; + while ((test = prev_active_insn_bb (bb, test)) != NULL) + { + rtx tnote; + /* Found a place that seems logical to adjust the stack. */ + tnote = find_reg_note (test, REG_ARGS_SIZE, NULL_RTX); + if (tnote) { - XEXP (SET_SRC (last), 1) - = GEN_INT (INTVAL (XEXP (SET_SRC (last), 1)) + this_adjust); + XEXP (tnote, 0) = XEXP (note, 0); return; } + if (prev_candidate == NULL) + prev_candidate = test; + /* Found something that affects unwinding. Stop searching. */ + if (CALL_P (test) || !insn_nothrow_p (test)) + break; + } - new_expr = gen_rtx_SEQUENCE (VOIDmode, - rtvec_alloc (XVECLEN (expr, 0) + 1)); - for (i = 0; i < XVECLEN (expr, 0); i++) - XVECEXP (new_expr, 0, i) = XVECEXP (expr, 0, i); - } + if (prev_candidate) + test = prev_candidate; + else if (next_candidate) + test = next_candidate; else { - new_expr = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2)); - if (note) - XVECEXP (new_expr, 0, 0) = XEXP (note, 0); - else - { - rtx expr = copy_rtx (single_set_for_csa (last_sp_set)); - - XEXP (SET_SRC (expr), 1) - = GEN_INT (INTVAL (XEXP (SET_SRC (expr), 1)) - this_adjust); - RTX_FRAME_RELATED_P (expr) = 1; - XVECEXP (new_expr, 0, 0) = expr; - } + /* ??? We *must* have a place, lest we ICE on the lost adjustment. + Options are: dummy clobber insn, nop, or prevent the removal of + the sp += 0 insn. */ + /* TODO: Find another way to indicate to the dwarf2 code that we + have not in fact lost an adjustment. */ + test = emit_insn_before (gen_rtx_CLOBBER (VOIDmode, const0_rtx), insn); } - - XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1) - = copy_rtx (single_set_for_csa (insn)); - RTX_FRAME_RELATED_P (XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1)) - = RTX_FRAME_RELATED_P (insn); - if (note) - XEXP (note, 0) = new_expr; - else - add_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, new_expr); + add_reg_note (test, REG_ARGS_SIZE, XEXP (note, 0)); } /* Subroutine of combine_stack_adjustments, called for each basic block. */ @@ -366,10 +492,11 @@ combine_stack_adjustments_for_block (basic_block bb) { HOST_WIDE_INT last_sp_adjust = 0; - rtx last_sp_set = NULL_RTX; + rtx_insn *last_sp_set = NULL; + rtx_insn *last2_sp_set = NULL; struct csa_reflist *reflist = NULL; - rtx insn, next, set; - struct record_stack_refs_data data; + rtx_insn *insn, *next; + rtx set; bool end_of_block = false; for (insn = BB_HEAD (bb); !end_of_block ; insn = next) @@ -381,6 +508,8 @@ continue; set = single_set_for_csa (insn); + if (set && find_reg_note (insn, REG_STACK_CHECK, NULL_RTX)) + set = NULL_RTX; if (set) { rtx dest = SET_DEST (set); @@ -427,14 +556,15 @@ /* Combine an allocation into the first instruction. */ if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0) { - if (try_apply_stack_adjustment (last_sp_set, reflist, - last_sp_adjust + this_adjust, - this_adjust)) + if (no_unhandled_cfa (insn) + && try_apply_stack_adjustment (last_sp_set, reflist, + last_sp_adjust + + this_adjust, + this_adjust)) { - if (RTX_FRAME_RELATED_P (last_sp_set)) - adjust_frame_related_expr (last_sp_set, insn, - this_adjust); /* It worked! */ + maybe_move_args_size_note (last_sp_set, insn, false); + maybe_merge_cfa_adjust (last_sp_set, insn, false); delete_insn (insn); last_sp_adjust += this_adjust; continue; @@ -446,11 +576,15 @@ else if (STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) { - if (try_apply_stack_adjustment (insn, reflist, - last_sp_adjust + this_adjust, - -last_sp_adjust)) + if (no_unhandled_cfa (last_sp_set) + && try_apply_stack_adjustment (insn, reflist, + last_sp_adjust + + this_adjust, + -last_sp_adjust)) { /* It worked! */ + maybe_move_args_size_note (insn, last_sp_set, true); + maybe_merge_cfa_adjust (insn, last_sp_set, true); delete_insn (last_sp_set); last_sp_set = insn; last_sp_adjust += this_adjust; @@ -463,8 +597,17 @@ /* Combination failed. Restart processing from here. If deallocation+allocation conspired to cancel, we can delete the old deallocation insn. */ - if (last_sp_set && last_sp_adjust == 0) - delete_insn (last_sp_set); + if (last_sp_set) + { + if (last_sp_adjust == 0 && no_unhandled_cfa (last_sp_set)) + { + maybe_move_args_size_note (insn, last_sp_set, true); + maybe_merge_cfa_adjust (insn, last_sp_set, true); + delete_insn (last_sp_set); + } + else + last2_sp_set = last_sp_set; + } free_csa_reflist (reflist); reflist = NULL; last_sp_set = insn; @@ -500,24 +643,22 @@ && try_apply_stack_adjustment (insn, reflist, 0, -last_sp_adjust)) { + if (last2_sp_set) + maybe_move_args_size_note (last2_sp_set, last_sp_set, false); + else + maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); free_csa_reflist (reflist); reflist = NULL; - last_sp_set = NULL_RTX; + last_sp_set = NULL; last_sp_adjust = 0; continue; } } - data.insn = insn; - data.reflist = reflist; if (!CALL_P (insn) && last_sp_set - && !for_each_rtx (&PATTERN (insn), record_stack_refs, &data)) - { - reflist = data.reflist; - continue; - } - reflist = data.reflist; + && record_stack_refs (insn, &reflist)) + continue; /* Otherwise, we were not able to process the instruction. Do not continue collecting data across such a one. */ @@ -526,65 +667,86 @@ || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))) { if (last_sp_set && last_sp_adjust == 0) - delete_insn (last_sp_set); + { + force_move_args_size_note (bb, last2_sp_set, last_sp_set); + delete_insn (last_sp_set); + } free_csa_reflist (reflist); reflist = NULL; - last_sp_set = NULL_RTX; + last2_sp_set = NULL; + last_sp_set = NULL; last_sp_adjust = 0; } } if (last_sp_set && last_sp_adjust == 0) - delete_insn (last_sp_set); + { + force_move_args_size_note (bb, last2_sp_set, last_sp_set); + delete_insn (last_sp_set); + } if (reflist) free_csa_reflist (reflist); } - -static bool -gate_handle_stack_adjustments (void) -{ - return flag_combine_stack_adjustments; -} - static unsigned int rest_of_handle_stack_adjustments (void) { - cleanup_cfg (flag_crossjumping ? CLEANUP_CROSSJUMP : 0); + df_note_add_problem (); + df_analyze (); + combine_stack_adjustments (); + return 0; +} + +namespace { +const pass_data pass_data_stack_adjustments = +{ + RTL_PASS, /* type */ + "csa", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_COMBINE_STACK_ADJUST, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish, /* todo_flags_finish */ +}; + +class pass_stack_adjustments : public rtl_opt_pass +{ +public: + pass_stack_adjustments (gcc::context *ctxt) + : rtl_opt_pass (pass_data_stack_adjustments, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *); + virtual unsigned int execute (function *) + { + return rest_of_handle_stack_adjustments (); + } + +}; // class pass_stack_adjustments + +bool +pass_stack_adjustments::gate (function *) +{ /* This is kind of a heuristic. We need to run combine_stack_adjustments even for machines with possibly nonzero TARGET_RETURN_POPS_ARGS and ACCUMULATE_OUTGOING_ARGS. We expect that only ports having push instructions will have popping returns. */ #ifndef PUSH_ROUNDING - if (!ACCUMULATE_OUTGOING_ARGS) + if (ACCUMULATE_OUTGOING_ARGS) + return false; #endif - { - df_note_add_problem (); - df_analyze (); - combine_stack_adjustments (); - } - return 0; + return flag_combine_stack_adjustments; } -struct rtl_opt_pass pass_stack_adjustments = +} // anon namespace + +rtl_opt_pass * +make_pass_stack_adjustments (gcc::context *ctxt) { - { - RTL_PASS, - "csa", /* name */ - gate_handle_stack_adjustments, /* gate */ - rest_of_handle_stack_adjustments, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_COMBINE_STACK_ADJUST, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_df_finish | TODO_verify_rtl_sharing | - TODO_dump_func | - TODO_ggc_collect, /* todo_flags_finish */ - } -}; + return new pass_stack_adjustments (ctxt); +}