This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Patch for noreturn/sibcall lossage
- To: gcc-patches at gcc dot gnu dot org
- Subject: Patch for noreturn/sibcall lossage
- From: Zack Weinberg <zack at wolery dot cumb dot org>
- Date: Mon, 3 Apr 2000 23:54:04 -0700
The sibcall optimizer confuses some of the warning-generation code in
the C front end: it doesn't get the 'noreturn function does return'
and related warnings right all the time. This patch attempts to fix
that.
The problem is that a function that ends with a sibcall will not be
considered to have reached its end. Therefore, if there are no other
exits from the function, the C front end will think it never returns.
My patch fixes this by having optimize_sibling_and_tail_recursive_calls
set a flag indicating that the function does sibcalls, which the front
end can pick up on. The flag might get set more often than it should;
I can't figure out how to tell the difference between recursion and
jumping to another function.
This fixes the gcc.dg/noreturn-1 failures. It may break
c-torture/special/eeprof-1; I am not sure whether that's actually
something unrelated, or what. If it is this patch, the culprit is
probably the tweak to c-decl.c to remember noreturn functions that
don't have an __attribute__ - I couldn't tell you why that's a
problem, though.
Other front end probably need to be adjusted to take advantage of
the new information.
zw
* flags.h (has_sibcall): New variable.
* rtl.h (NORETURN_CALL_PLACEHOLDER_P): New macro.
* sibcall.c: Define has_sibcall.
(optimize_sibling_and_tail_recursive_calls): If the function makes
at least one sibcall that will return to our caller, set
has_sibcall; otherwise, clear it. Use NORETURN_CALL_PLACEHOLDER_P
to detect sibcalls that don't return.
* calls.c (expand_call): Set NORETURN_CALL_PLACEHOLDER_P on call
placeholders.
* c-decl.c (finish_function): Check has_sibcall and set
current_function_returns_null or current_function_returns_value as
appropriate. If a function doesn't return and is declared to return
void, mark it as noreturn for future reference.
===================================================================
Index: c-decl.c
--- c-decl.c 2000/03/27 01:26:16 1.105
+++ c-decl.c 2000/04/04 06:46:26
@@ -6504,16 +6504,30 @@ finish_function (nested)
current_function_returns_null |= can_reach_end;
- if (warn_missing_noreturn
- && !TREE_THIS_VOLATILE (fndecl)
- && !current_function_returns_null
- && !current_function_returns_value)
- warning ("function might be possible candidate for attribute `noreturn'");
+ /* If the current function makes a sibcall, that means it returns
+ what it's supposed to return - either null or value. */
+ if (has_sibcall)
+ {
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl))) == void_type_node)
+ current_function_returns_null = 1;
+ else
+ current_function_returns_value = 1;
+ }
+ if (!current_function_returns_null && !current_function_returns_value)
+ {
+ if (warn_missing_noreturn && !TREE_THIS_VOLATILE (fndecl))
+ warning ("function might be a candidate for attribute `noreturn'");
+ /* If it's safe, mark the function noreturn now. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl))) == void_type_node)
+ TREE_THIS_VOLATILE (fndecl) = 1;
+ }
+
if (TREE_THIS_VOLATILE (fndecl) && current_function_returns_null)
warning ("`noreturn' function does return");
else if (warn_return_type && can_reach_end
- && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl))) != void_type_node)
+ && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (fndecl)))
+ != void_type_node))
/* If this function returns non-void and control can drop through,
complain. */
warning ("control reaches end of non-void function");
===================================================================
Index: calls.c
--- calls.c 2000/04/01 00:09:21 1.112
+++ calls.c 2000/04/04 06:46:27
@@ -3075,6 +3073,7 @@ expand_call (exp, target, ignore)
One of them will be selected later. */
if (tail_recursion_insns || tail_call_insns)
{
+ rtx callplace;
/* The tail recursion label must be kept around. We could expose
its use in the CALL_PLACEHOLDER, but that creates unwanted edges
and makes determining true tail recursion sites difficult.
@@ -3083,10 +3082,12 @@ expand_call (exp, target, ignore)
one of the call sequences after rtl generation is complete. */
if (tail_recursion_insns)
LABEL_PRESERVE_P (tail_recursion_label) = 1;
- emit_call_insn (gen_rtx_CALL_PLACEHOLDER (VOIDmode, normal_call_insns,
- tail_call_insns,
- tail_recursion_insns,
- tail_recursion_label));
+ callplace = gen_rtx_CALL_PLACEHOLDER (VOIDmode, normal_call_insns,
+ tail_call_insns,
+ tail_recursion_insns,
+ tail_recursion_label);
+ NORETURN_CALL_PLACEHOLDER_P (callplace) = is_volatile;
+ emit_call_insn (callplace);
}
else
emit_insns (normal_call_insns);
===================================================================
Index: flags.h
--- flags.h 2000/03/29 09:54:30 1.41
+++ flags.h 2000/04/04 06:46:27
@@ -522,6 +522,12 @@ extern int frame_pointer_needed;
extern int can_reach_end;
+/* Set nonzero if optimize_sibling_and_tail_recursive_calls finds that
+ this function makes a sibling call which returns (to this function's
+ caller). Unlike can_reach_end, the front end need not clear this. */
+
+extern int has_sibcall;
+
/* Nonzero if GCC must add code to check memory access (used by Checker). */
extern int flag_check_memory_usage;
===================================================================
Index: rtl.h
--- rtl.h 2000/03/31 16:24:30 1.183
+++ rtl.h 2000/04/04 06:46:28
@@ -142,6 +142,7 @@ typedef struct rtx_def
if it is deleted.
1 in a REG expression if corresponds to a variable declared by the user.
0 for an internally generated temporary.
+ In a CALL_PLACEHOLDER, 1 if the call does not return.
In a SYMBOL_REF, this flag is used for machine-specific purposes.
In a LABEL_REF or in a REG_LABEL note, this is LABEL_REF_NONLOCAL_P. */
unsigned int volatil : 1;
@@ -387,6 +388,9 @@ extern void rtvec_check_failed_bounds PA
/* 1 if insn (assumed to be a CALL_INSN) is a sibling call. */
#define SIBLING_CALL_P(INSN) ((INSN)->jump)
+
+/* 1 if pattern is a noreturn CALL_PLACEHOLDER. */
+#define NORETURN_CALL_PLACEHOLDER_P(PATTERN) ((PATTERN)->volatil)
/* 1 if insn is a branch that should not unconditionally execute its
delay slots, i.e., it is an annulled branch. */
===================================================================
Index: sibcall.c
--- sibcall.c 2000/03/28 21:03:37 1.4
+++ sibcall.c 2000/04/04 06:46:28
@@ -364,6 +364,11 @@ replace_call_placeholder (insn, use)
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
}
+/* optimize_sibling_and_tail_recursive_calls sets this flag if the
+ current function exits by making a sibcall. */
+
+int has_sibcall;
+
/* Given a (possibly empty) set of potential sibling or tail recursion call
sites, determine if optimization is possible.
@@ -380,6 +385,7 @@ optimize_sibling_and_tail_recursive_call
basic_block alternate_exit = EXIT_BLOCK_PTR;
int current_function_uses_addressof;
int successful_sibling_call = 0;
+ int sibcall_returns = 0;
int replaced_call_placeholder = 0;
edge e;
@@ -553,7 +559,11 @@ success:
a tail/sibling call. */
if (sibcall)
- successful_sibling_call = 1;
+ {
+ successful_sibling_call = 1;
+ if (! NORETURN_CALL_PLACEHOLDER_P (PATTERN (insn)))
+ sibcall_returns = 1;
+ }
replaced_call_placeholder = 1;
replace_call_placeholder (insn,
tailrecursion != 0
@@ -563,6 +573,8 @@ success:
: sibcall_use_normal);
}
}
+
+ has_sibcall = successful_sibling_call && sibcall_returns;
/* A sibling call sequence invalidates any REG_EQUIV notes made for
this function's incoming arguments.