Commit 0a1c58a2 by Jeffrey A Law Committed by Richard Henderson

Sibling call optimizations.

Co-Authored-By: Richard Henderson <rth@cygnus.com>

From-SVN: r32612
parent f1fd8077
2000-03-17 Jeff Law <law@cygnus.com>
Richard Henderson <rth@cygnus.com>
* Makefile.in (OBJS): Add sibcall.o.
(sibcall.o): New.
* sibcall.c: New file.
* calls.c (FUNCTION_OK_FOR_SIBCALL): Provide default.
(ECF_IS_CONST, ECF_NOTHROW, ECF_SIBCALL): New.
(emit_call_1): Replace `is_const' and `nothrow' with `ecf_flags'.
Emit sibcall patterns when requested. Update all callers.
(expand_call): Generate CALL_PLACEHOLDER insns when tail call
elimination seems feasable.
* final.c (leaf_function_p): Sibling calls don't discount being
a leaf function.
* flow.c (HAVE_sibcall_epilogue): Provide default.
(find_basic_blocks_1): Sibling calls don't throw.
(make_edges): Make edge from sibling call to EXIT.
(propagate_block): Don't remove sibcall_epilogue insns.
* function.c (prologue, epilogue): Turn into varrays. Update all uses.
(sibcall_epilogue): New.
(fixup_var_refs): Scan CALL_PLACEHOLDER sub-sequences.
(identify_blocks_1): Likewise. Break out from ...
(identify_blocks): ... here.
(reorder_blocks_1): Scan CALL_PLACEHOLDER. Break out from ...
(reorder_blocks): ... here.
(init_function_for_compilation): Zap prologue/epilogue as varrays.
(record_insns): Extend a varray instead of mallocing new memory.
(contains): Read a varray not array of ints.
(sibcall_epilogue_contains): New.
(thread_prologue_and_epilogue_insns): Emit and record
sibcall_epilogue patterns.
(init_function_once): Allocate prologue/epilogue varrays.
* genflags.c (gen_insn): Treat sibcall patterns as calls.
* integrate.c (save_parm_insns): Recurse on CALL_PLACEHOLDER patterns.
Broken out from ...
(save_for_inline_nocopy): ... here.
(copy_insn_list): Recurse on CALL_PLACEHOLDER patterns.
Broken out from ...
(expand_inline_function): ... here.
(copy_rtx_and_substitute): Handle NOTE_INSN_DELETED_LABEL.
(subst_constants): Handle 'n' formats.
* jump.c (jump_optimize_minimal): New.
(jump_optimize_1): New arg `minimal'; update callers. Elide most
optimizations if it's set.
* rtl.c (copy_rtx): Do copy jump & call for insns.
* rtl.h (struct rtx_def): Document use of jump and call for insns.
(SIBLING_CALL_P): New.
(sibcall_use_t): New.
* toplev.c (rest_of_compilation): Do init_EXPR_INSN_LIST_cache earlier.
Invoke optimize_sibling_and_tail_recursive_calls.
* tree.c (lang_safe_for_unsave): New.
(safe_for_unsave): New.
* tree.h (lang_safe_for_unsave, safe_for_unsave): Declare.
2000-03-17 Mark Mitchell <mark@codesourcery.com>
* objc/objc-act.c (encode_method_prototype): Pass types, not
......
......@@ -675,7 +675,8 @@ OBJS = diagnostic.o \
insn-opinit.o insn-recog.o insn-extract.o insn-output.o insn-emit.o lcm.o \
profile.o insn-attrtab.o $(out_object_file) $(EXTRA_OBJS) convert.o \
mbchar.o dyn-string.o splay-tree.o graph.o sbitmap.o resource.o hash.o \
predict.o lists.o ggc-common.o $(GGC) simplify-rtx.o ssa.o bb-reorder.o
predict.o lists.o ggc-common.o $(GGC) simplify-rtx.o ssa.o bb-reorder.o \
sibcall.o
# GEN files are listed separately, so they can be built before doing parallel
# makes for cc1 or cc1plus. Otherwise sequent parallel make attempts to load
......@@ -1562,6 +1563,8 @@ cse.o : cse.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h flags.h \
gcse.o : gcse.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h \
flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) \
function.h output.h toplev.h
sibcall.o : sibcall.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) function.h \
hard-reg-set.h flags.h insn-config.h $(RECOG_H) $(BASIC_BLOCK_H)
resource.o : resource.c $(CONFIG_H) $(RTL_H) hard-reg-set.h system.h \
$(BASIC_BLOCK_H) $(REGS_H) flags.h output.h resource.h function.h toplev.h \
insn-attr.h
......
......@@ -4019,7 +4019,8 @@ leaf_function_p ()
return 0;
if (GET_CODE (insn) == INSN
&& GET_CODE (PATTERN (insn)) == SEQUENCE
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN)
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN
&& ! SIBLING_CALL_P (XVECEXP (PATTERN (insn), 0, 0)))
return 0;
}
for (insn = current_function_epilogue_delay_list; insn; insn = XEXP (insn, 1))
......@@ -4028,7 +4029,8 @@ leaf_function_p ()
return 0;
if (GET_CODE (XEXP (insn, 0)) == INSN
&& GET_CODE (PATTERN (XEXP (insn, 0))) == SEQUENCE
&& GET_CODE (XVECEXP (PATTERN (XEXP (insn, 0)), 0, 0)) == CALL_INSN)
&& GET_CODE (XVECEXP (PATTERN (XEXP (insn, 0)), 0, 0)) == CALL_INSN
&& ! SIBLING_CALL_P (XVECEXP (PATTERN (XEXP (insn, 0)), 0, 0)))
return 0;
}
......
......@@ -154,10 +154,12 @@ Boston, MA 02111-1307, USA. */
#ifndef HAVE_epilogue
#define HAVE_epilogue 0
#endif
#ifndef HAVE_prologue
#define HAVE_prologue 0
#endif
#ifndef HAVE_sibcall_epilogue
#define HAVE_sibcall_epilogue 0
#endif
/* The contents of the current function definition are allocated
in this obstack, and all are freed at the end of the function.
......@@ -592,7 +594,8 @@ find_basic_blocks_1 (f)
does not imply an abnormal edge, it will be a bit before
everything can be updated. So continue to emit a noop at
the end of such a block. */
if (GET_CODE (end) == CALL_INSN)
if (GET_CODE (end) == CALL_INSN
&& ! SIBLING_CALL_P (end))
{
rtx nop = gen_rtx_USE (VOIDmode, const0_rtx);
end = emit_insn_after (nop, end);
......@@ -644,7 +647,8 @@ find_basic_blocks_1 (f)
imply an abnormal edge, it will be a bit before everything can
be updated. So continue to emit a noop at the end of such a
block. */
if (GET_CODE (end) == CALL_INSN)
if (GET_CODE (end) == CALL_INSN
&& ! SIBLING_CALL_P (end))
{
rtx nop = gen_rtx_USE (VOIDmode, const0_rtx);
end = emit_insn_after (nop, end);
......@@ -973,6 +977,15 @@ make_edges (label_value_list)
}
}
/* If this is a sibling call insn, then this is in effect a
combined call and return, and so we need an edge to the
exit block. No need to worry about EH edges, since we
wouldn't have created the sibling call in the first place. */
if (code == CALL_INSN && SIBLING_CALL_P (insn))
make_edge (edge_cache, bb, EXIT_BLOCK_PTR, 0);
else
/* If this is a CALL_INSN, then mark it as reaching the active EH
handler for this CALL_INSN. If we're handling asynchronous
exceptions then any insn can reach any of the active handlers.
......@@ -3249,8 +3262,10 @@ propagate_block (bb, old, significant, flags)
instructions. Warn about probable compiler losage. */
if (insn_is_dead
&& reload_completed
&& (HAVE_epilogue || HAVE_prologue)
&& prologue_epilogue_contains (insn))
&& (((HAVE_epilogue || HAVE_prologue)
&& prologue_epilogue_contains (insn))
|| (HAVE_sibcall_epilogue
&& sibcall_epilogue_contains (insn))))
{
if (flags & PROP_KILL_DEAD_CODE)
{
......
......@@ -174,11 +174,15 @@ gen_insn (insn)
call_value_pop) ignoring the extra arguments that are passed for
some machines, so by default, turn off the prototype. */
obstack_ptr = (name[0] == 'c'
obstack_ptr = ((name[0] == 'c' || name[0] == 's')
&& (!strcmp (name, "call")
|| !strcmp (name, "call_value")
|| !strcmp (name, "call_pop")
|| !strcmp (name, "call_value_pop")))
|| !strcmp (name, "call_value_pop")
|| !strcmp (name, "sibcall")
|| !strcmp (name, "sibcall_value")
|| !strcmp (name, "sibcall_pop")
|| !strcmp (name, "sibcall_value_pop")))
? &call_obstack : &normal_obstack;
obstack_grow (obstack_ptr, &insn, sizeof (rtx));
......
......@@ -125,13 +125,13 @@ static void delete_from_jump_chain PARAMS ((rtx));
static int delete_labelref_insn PARAMS ((rtx, rtx, int));
static void mark_modified_reg PARAMS ((rtx, rtx, void *));
static void redirect_tablejump PARAMS ((rtx, rtx));
static void jump_optimize_1 PARAMS ((rtx, int, int, int, int));
static void jump_optimize_1 PARAMS ((rtx, int, int, int, int, int));
#if ! defined(HAVE_cc0) && ! defined(HAVE_conditional_arithmetic)
static rtx find_insert_position PARAMS ((rtx, rtx));
#endif
static int returnjump_p_1 PARAMS ((rtx *, void *));
static void delete_prior_computation PARAMS ((rtx, rtx));
/* Main external entry point into the jump optimizer. See comments before
jump_optimize_1 for descriptions of the arguments. */
void
......@@ -141,7 +141,7 @@ jump_optimize (f, cross_jump, noop_moves, after_regscan)
int noop_moves;
int after_regscan;
{
jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, 0);
jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, 0, 0);
}
/* Alternate entry into the jump optimizer. This entry point only rebuilds
......@@ -151,9 +151,16 @@ void
rebuild_jump_labels (f)
rtx f;
{
jump_optimize_1 (f, 0, 0, 0, 1);
jump_optimize_1 (f, 0, 0, 0, 1, 0);
}
/* Alternate entry into the jump optimizer. Do only trivial optimizations. */
void
jump_optimize_minimal (f)
rtx f;
{
jump_optimize_1 (f, 0, 0, 0, 0, 1);
}
/* Delete no-op jumps and optimize jumps to jumps
and jumps around jumps.
......@@ -175,15 +182,29 @@ rebuild_jump_labels (f)
just determine whether control drops off the end of the function.
This case occurs when we have -W and not -O.
It works because `delete_insn' checks the value of `optimize'
and refrains from actually deleting when that is 0. */
and refrains from actually deleting when that is 0.
If MINIMAL is nonzero, then we only perform trivial optimizations:
* Removal of unreachable code after BARRIERs.
* Removal of unreferenced CODE_LABELs.
* Removal of a jump to the next instruction.
* Removal of a conditional jump followed by an unconditional jump
to the same target as the conditional jump.
* Simplify a conditional jump around an unconditional jump.
* Simplify a jump to a jump.
* Delete extraneous line number notes.
*/
static void
jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, mark_labels_only)
jump_optimize_1 (f, cross_jump, noop_moves, after_regscan,
mark_labels_only, minimal)
rtx f;
int cross_jump;
int noop_moves;
int after_regscan;
int mark_labels_only;
int minimal;
{
register rtx insn, next;
int changed;
......@@ -230,7 +251,8 @@ jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, mark_labels_only)
if (mark_labels_only)
goto end;
exception_optimize ();
if (! minimal)
exception_optimize ();
last_insn = delete_unreferenced_labels (f);
......@@ -320,7 +342,7 @@ jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, mark_labels_only)
if (nlabel != JUMP_LABEL (insn))
changed |= redirect_jump (insn, nlabel);
if (! optimize)
if (! optimize || ! minimal)
continue;
/* If a dispatch table always goes to the same place,
......@@ -2135,7 +2157,7 @@ jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, mark_labels_only)
not be cleared. This is especially true for the case where we
delete the NOTE_FUNCTION_END note. CAN_REACH_END is cleared by
the front-end before compiling each function. */
if (calculate_can_reach_end (last_insn, optimize != 0))
if (! minimal && calculate_can_reach_end (last_insn, optimize != 0))
can_reach_end = 1;
end:
......
......@@ -406,14 +406,12 @@ copy_rtx (orig)
walks over the RTL. */
copy->used = 0;
/* We do not copy JUMP, CALL, or FRAME_RELATED for INSNs. */
/* We do not copy FRAME_RELATED for INSNs. */
if (GET_RTX_CLASS (code) == 'i')
{
copy->jump = 0;
copy->call = 0;
copy->frame_related = 0;
}
copy->frame_related = 0;
copy->jump = orig->jump;
copy->call = orig->call;
format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
......
/* This file contains the definitions and documentation for the
Register Transfer Expressions (rtx's) that make up the
Register Transfer Language (rtl) used in the Back End of the GNU compiler.
Copyright (C) 1987, 88, 92, 94, 95, 97, 98, 1999
Copyright (C) 1987, 88, 92, 94, 95, 97, 98, 1999, 2000
Free Software Foundation, Inc.
This file is part of GNU CC.
......@@ -880,7 +880,10 @@ DEF_RTL_EXPR(CONSTANT_P_RTX, "constant_p_rtx", "e", 'x')
potential tail recursive calls were found.
The tail recursion label is needed so that we can clear LABEL_PRESERVE_P
after we select a call method. */
after we select a call method.
This method of tail-call elimination is intended to be replaced by
tree-based optimizations once front-end conversions are complete. */
DEF_RTL_EXPR(CALL_PLACEHOLDER, "call_placeholder", "uuuu", 'x')
/* The SSA phi operator.
......
......@@ -119,9 +119,12 @@ typedef struct rtx_def
#else
enum machine_mode mode : 8;
#endif
/* LINK_COST_ZERO in an INSN_LIST. */
/* 1 in an INSN if it can alter flow of control
within this function.
LINK_COST_ZERO in an INSN_LIST. */
unsigned int jump : 1;
/* LINK_COST_FREE in an INSN_LIST. */
/* 1 in an INSN if it can call another function.
LINK_COST_FREE in an INSN_LIST. */
unsigned int call : 1;
/* 1 in a MEM or REG if value of this expression will never change
during the current function, even though it is not
......@@ -380,6 +383,9 @@ extern void rtvec_check_failed_bounds PARAMS ((rtvec, int,
/* 1 if insn is a call to a const function. */
#define CONST_CALL_P(INSN) ((INSN)->unchanging)
/* 1 if insn (assumed to be a CALL_INSN) is a sibling call. */
#define SIBLING_CALL_P(INSN) ((INSN)->jump)
/* 1 if insn is a branch that should not unconditionally execute its
delay slots, i.e., it is an annulled branch. */
#define INSN_ANNULLED_BRANCH_P(INSN) ((INSN)->unchanging)
......@@ -1416,6 +1422,7 @@ extern int rtx_renumbered_equal_p PARAMS ((rtx, rtx));
extern int true_regnum PARAMS ((rtx));
extern int redirect_jump PARAMS ((rtx, rtx));
extern void jump_optimize PARAMS ((rtx, int, int, int));
extern void jump_optimize_minimal PARAMS ((rtx));
extern void rebuild_jump_labels PARAMS ((rtx));
extern void thread_jumps PARAMS ((rtx, int, int));
extern int redirect_exp PARAMS ((rtx *, rtx, rtx, rtx));
......@@ -1513,6 +1520,7 @@ extern void record_excess_regs PARAMS ((rtx, rtx, rtx *));
extern void reposition_prologue_and_epilogue_notes PARAMS ((rtx));
extern void thread_prologue_and_epilogue_insns PARAMS ((rtx));
extern int prologue_epilogue_contains PARAMS ((rtx));
extern int sibcall_epilogue_contains PARAMS ((rtx));
extern HOST_WIDE_INT get_frame_size PARAMS ((void));
extern void preserve_rtl_expr_result PARAMS ((rtx));
extern void mark_temp_addr_taken PARAMS ((rtx));
......@@ -1713,6 +1721,16 @@ extern void record_base_value PARAMS ((int, rtx, int));
extern void record_alias_subset PARAMS ((int, int));
extern rtx addr_side_effect_eval PARAMS ((rtx, int, int));
/* In sibcall.c */
typedef enum {
sibcall_use_normal = 1,
sibcall_use_tail_recursion,
sibcall_use_sibcall
} sibcall_use_t;
extern void optimize_sibling_and_tail_recursive_calls PARAMS ((void));
extern void replace_call_placeholder PARAMS ((rtx, sibcall_use_t));
#ifdef STACK_REGS
extern int stack_regs_mentioned PARAMS ((rtx insn));
#endif
......
This diff is collapsed. Click to expand it.
......@@ -2986,6 +2986,15 @@ rest_of_compilation (decl)
goto exit_rest_of_compilation;
}
/* We may have potential sibling or tail recursion sites. Select one
(of possibly multiple) methods of performing the call. */
init_EXPR_INSN_LIST_cache ();
if (optimize)
optimize_sibling_and_tail_recursive_calls ();
if (ggc_p)
ggc_collect ();
/* Initialize some variables used by the optimizers. */
init_function_for_compilation ();
......@@ -3030,8 +3039,6 @@ rest_of_compilation (decl)
unshare_all_rtl (current_function_decl, insns);
init_EXPR_INSN_LIST_cache ();
#ifdef SETJMP_VIA_SAVE_AREA
/* This must be performed before virutal register instantiation. */
if (current_function_calls_alloca)
......
......@@ -285,6 +285,9 @@ static void mark_type_hash PARAMS ((void *));
void (*lang_unsave) PARAMS ((tree *));
void (*lang_unsave_expr_now) PARAMS ((tree));
/* If non-null, a language specific version of safe_for_unsave. */
int (*lang_safe_for_unsave) PARAMS ((tree));
/* The string used as a placeholder instead of a source file name for
built-in tree nodes. The variable, which is dynamically allocated,
should be used; the macro is only used to initialize it. */
......@@ -2666,6 +2669,82 @@ unsave_expr_now (expr)
return expr;
}
/* Return nonzero if it is safe to unsave EXPR, else return zero.
It is not safe to unsave EXPR if it contains any embedded RTL_EXPRs. */
int
safe_for_unsave (expr)
tree expr;
{
enum tree_code code;
register int i;
int first_rtl;
if (expr == NULL_TREE)
return 1;
code = TREE_CODE (expr);
first_rtl = first_rtl_op (code);
switch (code)
{
case RTL_EXPR:
return 0;
case CALL_EXPR:
if (TREE_OPERAND (expr, 1)
&& TREE_CODE (TREE_OPERAND (expr, 1)) == TREE_LIST)
{
tree exp = TREE_OPERAND (expr, 1);
while (exp)
{
if (! safe_for_unsave (TREE_VALUE (exp)))
return 0;
exp = TREE_CHAIN (exp);
}
}
break;
default:
if (lang_safe_for_unsave)
switch ((*lang_safe_for_unsave) (expr))
{
case -1:
break;
case 0:
return 0;
case 1:
return 1;
default:
abort ();
}
break;
}
switch (TREE_CODE_CLASS (code))
{
case 'c': /* a constant */
case 't': /* a type node */
case 'x': /* something random, like an identifier or an ERROR_MARK. */
case 'd': /* A decl node */
case 'b': /* A block node */
return 1;
case 'e': /* an expression */
case 'r': /* a reference */
case 's': /* an expression with side effects */
case '<': /* a comparison expression */
case '2': /* a binary arithmetic expression */
case '1': /* a unary arithmetic expression */
for (i = first_rtl - 1; i >= 0; i--)
if (! safe_for_unsave (TREE_OPERAND (expr, i)))
return 0;
return 1;
default:
return 0;
}
}
/* Return 1 if EXP contains a PLACEHOLDER_EXPR; i.e., if it represents a size
or offset that depends on a field within a record. */
......
......@@ -1983,6 +1983,11 @@ extern int first_rtl_op PARAMS ((enum tree_code));
extern tree unsave_expr PARAMS ((tree));
/* safe_for_reeval_p (EXP) returns nonzero if it is possible to
expand EXP multiple times. */
extern int safe_for_reeval_p PARAMS ((tree));
/* Reset EXP in place so that it can be expaned again. Does not
recurse into subtrees. */
......@@ -2000,6 +2005,9 @@ extern tree unsave_expr_now PARAMS ((tree));
extern void (*lang_unsave) PARAMS ((tree *));
extern void (*lang_unsave_expr_now) PARAMS ((tree));
/* If non-null, a language specific version of safe_for_unsave. */
extern int (*lang_safe_for_unsave) PARAMS ((tree));
/* Return 1 if EXP contains a PLACEHOLDER_EXPR; i.e., if it represents a size
or offset that depends on a field within a record.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment