diff options
Diffstat (limited to 'contrib/gcc/sibcall.c')
-rw-r--r-- | contrib/gcc/sibcall.c | 759 |
1 files changed, 0 insertions, 759 deletions
diff --git a/contrib/gcc/sibcall.c b/contrib/gcc/sibcall.c deleted file mode 100644 index 1d647f1..0000000 --- a/contrib/gcc/sibcall.c +++ /dev/null @@ -1,759 +0,0 @@ -/* Generic sibling call optimization support - Copyright (C) 1999, 2000, 2001, 2002, 2003 - Free Software Foundation, Inc. - -This file is part of GCC. - -GCC is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later -version. - -GCC is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tm.h" - -#include "rtl.h" -#include "regs.h" -#include "function.h" -#include "hard-reg-set.h" -#include "flags.h" -#include "insn-config.h" -#include "recog.h" -#include "basic-block.h" -#include "output.h" -#include "except.h" -#include "tree.h" - -/* In case alternate_exit_block contains copy from pseudo, to return value, - record the pseudo here. In such case the pseudo must be set to function - return in the sibcall sequence. */ -static rtx return_value_pseudo; - -static int identify_call_return_value (rtx, rtx *, rtx *); -static rtx skip_copy_to_return_value (rtx); -static rtx skip_use_of_return_value (rtx, enum rtx_code); -static rtx skip_stack_adjustment (rtx); -static rtx skip_pic_restore (rtx); -static rtx skip_jump_insn (rtx); -static int call_ends_block_p (rtx, rtx); -static int uses_addressof (rtx); -static int sequence_uses_addressof (rtx); -static void purge_reg_equiv_notes (void); -static void purge_mem_unchanging_flag (rtx); -static rtx skip_unreturned_value (rtx); - -/* Examine a CALL_PLACEHOLDER pattern and determine where the call's - return value is located. P_HARD_RETURN receives the hard register - that the function used; P_SOFT_RETURN receives the pseudo register - that the sequence used. Return nonzero if the values were located. */ - -static int -identify_call_return_value (rtx cp, rtx *p_hard_return, rtx *p_soft_return) -{ - rtx insn, set, hard, soft; - - insn = XEXP (cp, 0); - /* Search backward through the "normal" call sequence to the CALL insn. */ - while (NEXT_INSN (insn)) - insn = NEXT_INSN (insn); - while (GET_CODE (insn) != CALL_INSN) - insn = PREV_INSN (insn); - - /* Assume the pattern is (set (dest) (call ...)), or that the first - member of a parallel is. This is the hard return register used - by the function. */ - if (GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_SRC (PATTERN (insn))) == CALL) - hard = SET_DEST (PATTERN (insn)); - else if (GET_CODE (PATTERN (insn)) == PARALLEL - && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET - && GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == CALL) - hard = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); - else - return 0; - - /* If we didn't get a single hard register (e.g. a parallel), give up. */ - if (GET_CODE (hard) != REG) - return 0; - - /* Stack adjustment done after call may appear here. */ - insn = skip_stack_adjustment (insn); - if (! insn) - return 0; - - /* Restore of GP register may appear here. */ - insn = skip_pic_restore (insn); - if (! insn) - return 0; - - /* If there's nothing after, there's no soft return value. */ - insn = NEXT_INSN (insn); - if (! insn) - return 0; - - /* We're looking for a source of the hard return register. */ - set = single_set (insn); - if (! set || SET_SRC (set) != hard) - return 0; - - soft = SET_DEST (set); - insn = NEXT_INSN (insn); - - /* Allow this first destination to be copied to a second register, - as might happen if the first register wasn't the particular pseudo - we'd been expecting. */ - if (insn - && (set = single_set (insn)) != NULL_RTX - && SET_SRC (set) == soft) - { - soft = SET_DEST (set); - insn = NEXT_INSN (insn); - } - - /* Don't fool with anything but pseudo registers. */ - if (GET_CODE (soft) != REG || REGNO (soft) < FIRST_PSEUDO_REGISTER) - return 0; - - /* This value must not be modified before the end of the sequence. */ - if (reg_set_between_p (soft, insn, NULL_RTX)) - return 0; - - *p_hard_return = hard; - *p_soft_return = soft; - - return 1; -} - -/* If the first real insn after ORIG_INSN copies to this function's - return value from RETVAL, then return the insn which performs the - copy. Otherwise return ORIG_INSN. */ - -static rtx -skip_copy_to_return_value (rtx orig_insn) -{ - rtx insn, set = NULL_RTX; - rtx hardret, softret; - - /* If there is no return value, we have nothing to do. */ - if (! identify_call_return_value (PATTERN (orig_insn), &hardret, &softret)) - return orig_insn; - - insn = next_nonnote_insn (orig_insn); - if (! insn) - return orig_insn; - - set = single_set (insn); - if (! set) - return orig_insn; - - if (return_value_pseudo) - { - if (SET_DEST (set) == return_value_pseudo - && SET_SRC (set) == softret) - return insn; - return orig_insn; - } - - /* The destination must be the same as the called function's return - value to ensure that any return value is put in the same place by the - current function and the function we're calling. - - Further, the source must be the same as the pseudo into which the - called function's return value was copied. Otherwise we're returning - some other value. */ - -#ifndef OUTGOING_REGNO -#define OUTGOING_REGNO(N) (N) -#endif - - if (SET_DEST (set) == current_function_return_rtx - && REG_P (SET_DEST (set)) - && OUTGOING_REGNO (REGNO (SET_DEST (set))) == REGNO (hardret) - && SET_SRC (set) == softret) - return insn; - - /* Recognize the situation when the called function's return value - is copied in two steps: first into an intermediate pseudo, then - the into the calling functions return value register. */ - - if (REG_P (SET_DEST (set)) - && SET_SRC (set) == softret) - { - rtx x = SET_DEST (set); - - insn = next_nonnote_insn (insn); - if (! insn) - return orig_insn; - - set = single_set (insn); - if (! set) - return orig_insn; - - if (SET_DEST (set) == current_function_return_rtx - && REG_P (SET_DEST (set)) - && OUTGOING_REGNO (REGNO (SET_DEST (set))) == REGNO (hardret) - && SET_SRC (set) == x) - return insn; - } - - /* It did not look like a copy of the return value, so return the - same insn we were passed. */ - return orig_insn; -} - -/* If the first real insn after ORIG_INSN is a CODE of this function's return - value, return insn. Otherwise return ORIG_INSN. */ - -static rtx -skip_use_of_return_value (rtx orig_insn, enum rtx_code code) -{ - rtx insn; - - insn = next_nonnote_insn (orig_insn); - - if (insn - && GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == code - && (XEXP (PATTERN (insn), 0) == current_function_return_rtx - || XEXP (PATTERN (insn), 0) == const0_rtx)) - return insn; - - return orig_insn; -} - -/* In case function does not return value, we get clobber of pseudo followed - by set to hard return value. */ -static rtx -skip_unreturned_value (rtx orig_insn) -{ - rtx insn = next_nonnote_insn (orig_insn); - - /* Skip possible clobber of pseudo return register. */ - if (insn - && GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == CLOBBER - && REG_P (XEXP (PATTERN (insn), 0)) - && (REGNO (XEXP (PATTERN (insn), 0)) >= FIRST_PSEUDO_REGISTER)) - { - rtx set_insn = next_nonnote_insn (insn); - rtx set; - if (!set_insn) - return insn; - set = single_set (set_insn); - if (!set - || SET_SRC (set) != XEXP (PATTERN (insn), 0) - || SET_DEST (set) != current_function_return_rtx) - return insn; - return set_insn; - } - return orig_insn; -} - -/* If the first real insn after ORIG_INSN adjusts the stack pointer - by a constant, return the insn with the stack pointer adjustment. - Otherwise return ORIG_INSN. */ - -static rtx -skip_stack_adjustment (rtx orig_insn) -{ - rtx insn, set = NULL_RTX; - - insn = next_nonnote_insn (orig_insn); - - if (insn) - set = single_set (insn); - - if (insn - && set - && GET_CODE (SET_SRC (set)) == PLUS - && XEXP (SET_SRC (set), 0) == stack_pointer_rtx - && GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT - && SET_DEST (set) == stack_pointer_rtx) - return insn; - - return orig_insn; -} - -/* If the first real insn after ORIG_INSN sets the pic register, - return it. Otherwise return ORIG_INSN. */ - -static rtx -skip_pic_restore (rtx orig_insn) -{ - rtx insn, set = NULL_RTX; - - insn = next_nonnote_insn (orig_insn); - - if (insn) - set = single_set (insn); - - if (insn && set && SET_DEST (set) == pic_offset_table_rtx) - return insn; - - return orig_insn; -} - -/* If the first real insn after ORIG_INSN is a jump, return the JUMP_INSN. - Otherwise return ORIG_INSN. */ - -static rtx -skip_jump_insn (rtx orig_insn) -{ - rtx insn; - - insn = next_nonnote_insn (orig_insn); - - if (insn - && GET_CODE (insn) == JUMP_INSN - && any_uncondjump_p (insn)) - return insn; - - return orig_insn; -} - -/* Using the above functions, see if INSN, skipping any of the above, - goes all the way to END, the end of a basic block. Return 1 if so. */ - -static int -call_ends_block_p (rtx insn, rtx end) -{ - rtx new_insn; - - /* END might be a note, so get the last nonnote insn of the block. */ - if (NOTE_P (end)) - end = prev_nonnote_insn (end); - - /* If the call was the end of the block, then we're OK. */ - if (insn == end) - return 1; - - /* Skip over copying from the call's return value pseudo into - this function's hard return register and if that's the end - of the block, we're OK. */ - new_insn = skip_copy_to_return_value (insn); - - /* In case we return value in pseudo, we must set the pseudo to - return value of called function, otherwise we are returning - something else. */ - if (return_value_pseudo && insn == new_insn) - return 0; - insn = new_insn; - - if (insn == end) - return 1; - - /* Skip any stack adjustment. */ - insn = skip_stack_adjustment (insn); - if (insn == end) - return 1; - - /* Skip over a CLOBBER of the return value as a hard reg. */ - insn = skip_use_of_return_value (insn, CLOBBER); - if (insn == end) - return 1; - - /* Skip over a CLOBBER of the return value as a hard reg. */ - insn = skip_unreturned_value (insn); - if (insn == end) - return 1; - - /* Skip over a USE of the return value (as a hard reg). */ - insn = skip_use_of_return_value (insn, USE); - if (insn == end) - return 1; - - /* Skip over a JUMP_INSN at the end of the block. If that doesn't end the - block, the original CALL_INSN didn't. */ - insn = skip_jump_insn (insn); - return insn == end; -} - -/* Scan the rtx X for ADDRESSOF expressions or - current_function_internal_arg_pointer registers. - Return nonzero if an ADDRESSOF or current_function_internal_arg_pointer - is found outside of some MEM expression, else return zero. */ - -static int -uses_addressof (rtx x) -{ - RTX_CODE code; - int i, j; - const char *fmt; - - if (x == NULL_RTX) - return 0; - - code = GET_CODE (x); - - if (code == ADDRESSOF || x == current_function_internal_arg_pointer) - return 1; - - if (code == MEM) - return 0; - - /* Scan all subexpressions. */ - fmt = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) - { - if (*fmt == 'e') - { - if (uses_addressof (XEXP (x, i))) - return 1; - } - else if (*fmt == 'E') - { - for (j = 0; j < XVECLEN (x, i); j++) - if (uses_addressof (XVECEXP (x, i, j))) - return 1; - } - } - return 0; -} - -/* Scan the sequence of insns in SEQ to see if any have an ADDRESSOF - rtl expression or current_function_internal_arg_pointer occurrences - not enclosed within a MEM. If an ADDRESSOF expression or - current_function_internal_arg_pointer is found, return nonzero, otherwise - return zero. - - This function handles CALL_PLACEHOLDERs which contain multiple sequences - of insns. */ - -static int -sequence_uses_addressof (rtx seq) -{ - rtx insn; - - for (insn = seq; insn; insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - { - /* If this is a CALL_PLACEHOLDER, then recursively call ourselves - with each nonempty sequence attached to the CALL_PLACEHOLDER. */ - if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) - { - if (XEXP (PATTERN (insn), 0) != NULL_RTX - && sequence_uses_addressof (XEXP (PATTERN (insn), 0))) - return 1; - if (XEXP (PATTERN (insn), 1) != NULL_RTX - && sequence_uses_addressof (XEXP (PATTERN (insn), 1))) - return 1; - if (XEXP (PATTERN (insn), 2) != NULL_RTX - && sequence_uses_addressof (XEXP (PATTERN (insn), 2))) - return 1; - } - else if (uses_addressof (PATTERN (insn)) - || (REG_NOTES (insn) && uses_addressof (REG_NOTES (insn)))) - return 1; - } - return 0; -} - -/* Remove all REG_EQUIV notes found in the insn chain. */ - -static void -purge_reg_equiv_notes (void) -{ - rtx insn; - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - { - while (1) - { - rtx note = find_reg_note (insn, REG_EQUIV, 0); - if (note) - { - /* Remove the note and keep looking at the notes for - this insn. */ - remove_note (insn, note); - continue; - } - break; - } - } -} - -/* Clear RTX_UNCHANGING_P flag of incoming argument MEMs. */ - -static void -purge_mem_unchanging_flag (rtx x) -{ - RTX_CODE code; - int i, j; - const char *fmt; - - if (x == NULL_RTX) - return; - - code = GET_CODE (x); - - if (code == MEM) - { - if (RTX_UNCHANGING_P (x) - && (XEXP (x, 0) == current_function_internal_arg_pointer - || (GET_CODE (XEXP (x, 0)) == PLUS - && XEXP (XEXP (x, 0), 0) == - current_function_internal_arg_pointer - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))) - RTX_UNCHANGING_P (x) = 0; - return; - } - - /* Scan all subexpressions. */ - fmt = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++) - { - if (*fmt == 'e') - purge_mem_unchanging_flag (XEXP (x, i)); - else if (*fmt == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - purge_mem_unchanging_flag (XVECEXP (x, i, j)); - } -} - -/* Replace the CALL_PLACEHOLDER with one of its children. INSN should be - the CALL_PLACEHOLDER insn; USE tells which child to use. */ - -void -replace_call_placeholder (rtx insn, sibcall_use_t use) -{ - if (use == sibcall_use_tail_recursion) - emit_insn_before (XEXP (PATTERN (insn), 2), insn); - else if (use == sibcall_use_sibcall) - emit_insn_before (XEXP (PATTERN (insn), 1), insn); - else if (use == sibcall_use_normal) - emit_insn_before (XEXP (PATTERN (insn), 0), insn); - else - abort (); - - /* Turn off LABEL_PRESERVE_P for the tail recursion label if it - exists. We only had to set it long enough to keep the jump - pass above from deleting it as unused. */ - if (XEXP (PATTERN (insn), 3)) - LABEL_PRESERVE_P (XEXP (PATTERN (insn), 3)) = 0; - - /* "Delete" the placeholder insn. */ - remove_insn (insn); -} - -/* Given a (possibly empty) set of potential sibling or tail recursion call - sites, determine if optimization is possible. - - Potential sibling or tail recursion calls are marked with CALL_PLACEHOLDER - insns. The CALL_PLACEHOLDER insn holds chains of insns to implement a - normal call, sibling call or tail recursive call. - - Replace the CALL_PLACEHOLDER with an appropriate insn chain. */ - -void -optimize_sibling_and_tail_recursive_calls (void) -{ - rtx insn, insns; - basic_block alternate_exit = EXIT_BLOCK_PTR; - bool no_sibcalls_this_function = false; - bool successful_replacement = false; - bool replaced_call_placeholder = false; - edge e; - - insns = get_insns (); - - cleanup_cfg (CLEANUP_PRE_SIBCALL | CLEANUP_PRE_LOOP); - - /* If there are no basic blocks, then there is nothing to do. */ - if (n_basic_blocks == 0) - return; - - /* If we are using sjlj exceptions, we may need to add a call to - _Unwind_SjLj_Unregister at exit of the function. Which means - that we cannot do any sibcall transformations. */ - if (USING_SJLJ_EXCEPTIONS && current_function_has_exception_handlers ()) - no_sibcalls_this_function = true; - - return_value_pseudo = NULL_RTX; - - /* Find the exit block. - - It is possible that we have blocks which can reach the exit block - directly. However, most of the time a block will jump (or fall into) - N_BASIC_BLOCKS - 1, which in turn falls into the exit block. */ - for (e = EXIT_BLOCK_PTR->pred; - e && alternate_exit == EXIT_BLOCK_PTR; - e = e->pred_next) - { - rtx insn; - - if (e->dest != EXIT_BLOCK_PTR || e->succ_next != NULL) - continue; - - /* Walk forwards through the last normal block and see if it - does nothing except fall into the exit block. */ - for (insn = BB_HEAD (EXIT_BLOCK_PTR->prev_bb); - insn; - insn = NEXT_INSN (insn)) - { - rtx set; - /* This should only happen once, at the start of this block. */ - if (GET_CODE (insn) == CODE_LABEL) - continue; - - if (GET_CODE (insn) == NOTE) - continue; - - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == USE) - continue; - - /* Exit block also may contain copy from pseudo containing - return value to hard register. */ - if (GET_CODE (insn) == INSN - && (set = single_set (insn)) - && SET_DEST (set) == current_function_return_rtx - && REG_P (SET_SRC (set)) - && !return_value_pseudo) - { - return_value_pseudo = SET_SRC (set); - continue; - } - - break; - } - - /* If INSN is zero, then the search walked all the way through the - block without hitting anything interesting. This block is a - valid alternate exit block. */ - if (insn == NULL) - alternate_exit = e->src; - else - return_value_pseudo = NULL; - } - - /* If the function uses ADDRESSOF, we can't (easily) determine - at this point if the value will end up on the stack. */ - no_sibcalls_this_function |= sequence_uses_addressof (insns); - - /* Walk the insn chain and find any CALL_PLACEHOLDER insns. We need to - select one of the insn sequences attached to each CALL_PLACEHOLDER. - - The different sequences represent different ways to implement the call, - ie, tail recursion, sibling call or normal call. - - Since we do not create nested CALL_PLACEHOLDERs, the scan - continues with the insn that was after a replaced CALL_PLACEHOLDER; - we don't rescan the replacement insns. */ - for (insn = insns; insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) - { - int sibcall = (XEXP (PATTERN (insn), 1) != NULL_RTX); - int tailrecursion = (XEXP (PATTERN (insn), 2) != NULL_RTX); - basic_block call_block = BLOCK_FOR_INSN (insn); - - /* alloca (until we have stack slot life analysis) inhibits - sibling call optimizations, but not tail recursion. - Similarly if we use varargs or stdarg since they implicitly - may take the address of an argument. */ - if (current_function_calls_alloca || current_function_stdarg) - sibcall = 0; - - /* See if there are any reasons we can't perform either sibling or - tail call optimizations. We must be careful with stack slots - which are live at potential optimization sites. */ - if (no_sibcalls_this_function - /* ??? Overly conservative. */ - || frame_offset - /* Any function that calls setjmp might have longjmp called from - any called function. ??? We really should represent this - properly in the CFG so that this needn't be special cased. */ - || current_function_calls_setjmp - /* Can't if more than one successor or single successor is not - exit block. These two tests prevent tail call optimization - in the presence of active exception handlers. */ - || call_block->succ == NULL - || call_block->succ->succ_next != NULL - || (call_block->succ->dest != EXIT_BLOCK_PTR - && call_block->succ->dest != alternate_exit) - /* If this call doesn't end the block, there are operations at - the end of the block which we must execute after returning. */ - || ! call_ends_block_p (insn, BB_END (call_block))) - sibcall = 0, tailrecursion = 0; - - /* Select a set of insns to implement the call and emit them. - Tail recursion is the most efficient, so select it over - a tail/sibling call. */ - - if (sibcall || tailrecursion) - successful_replacement = true; - replaced_call_placeholder = true; - - replace_call_placeholder (insn, - tailrecursion != 0 - ? sibcall_use_tail_recursion - : sibcall != 0 - ? sibcall_use_sibcall - : sibcall_use_normal); - } - } - - if (successful_replacement) - { - rtx insn; - tree arg; - - /* A sibling call sequence invalidates any REG_EQUIV notes made for - this function's incoming arguments. - - At the start of RTL generation we know the only REG_EQUIV notes - in the rtl chain are those for incoming arguments, so we can safely - flush any REG_EQUIV note. - - This is (slight) overkill. We could keep track of the highest - argument we clobber and be more selective in removing notes, but it - does not seem to be worth the effort. */ - purge_reg_equiv_notes (); - - /* A sibling call sequence also may invalidate RTX_UNCHANGING_P - flag of some incoming arguments MEM RTLs, because it can write into - those slots. We clear all those bits now. - - This is (slight) overkill, we could keep track of which arguments - we actually write into. */ - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - { - if (INSN_P (insn)) - purge_mem_unchanging_flag (PATTERN (insn)); - } - - /* Similarly, invalidate RTX_UNCHANGING_P for any incoming - arguments passed in registers. */ - for (arg = DECL_ARGUMENTS (current_function_decl); - arg; - arg = TREE_CHAIN (arg)) - { - if (REG_P (DECL_RTL (arg))) - RTX_UNCHANGING_P (DECL_RTL (arg)) = false; - } - } - - /* There may have been NOTE_INSN_BLOCK_{BEGIN,END} notes in the - CALL_PLACEHOLDER alternatives that we didn't emit. Rebuild the - lexical block tree to correspond to the notes that still exist. */ - if (replaced_call_placeholder) - reorder_blocks (); - - /* This information will be invalid after inline expansion. Kill it now. */ - free_basic_block_vars (0); - free_EXPR_LIST_list (&tail_recursion_label_list); -} |