+2011-03-21 Georg-Johann Lay <avr@gjlay.de>
+
+ * config/avr/avr-protos.h (expand_epilogue): Change prototype
+ * config/avr/avr.h (struct machine_function): Add field sibcall_fails.
+ * config/avr/avr.c (init_cumulative_args)
+ (avr_function_arg_advance): Use it.
+ * config/avr/avr.c (expand_epilogue): Add bool parameter. Handle
+ sibcall epilogues.
+ (TARGET_FUNCTION_OK_FOR_SIBCALL): Define to...
+ (avr_function_ok_for_sibcall): ...this new function.
+ (avr_lookup_function_attribute1): New static Function.
+ (avr_naked_function_p, interrupt_function_p)
+ (signal_function_p, avr_OS_task_function_p)
+ (avr_OS_main_function_p): Use it.
+ * config/avr/avr.md ("sibcall", "sibcall_value")
+ ("sibcall_epilogue"): New expander.
+ ("*call_insn", "*call_value_insn"): New insn.
+ ("call_insn", "call_value_insn"): Remove
+ ("call", "call_value", "epilogue"): Change expander to handle
+ sibling calls.
+
2011-03-21 Nick Clifton <nickc@redhat.com>
* doc/invoke.texi (Overall Options): Move closing brace to end of
extern bool avr_rotate_bytes (rtx operands[]);
extern void expand_prologue (void);
-extern void expand_epilogue (void);
+extern void expand_epilogue (bool);
extern int avr_epilogue_uses (int regno);
extern void avr_output_bld (rtx operands[], int bit_nr);
static void avr_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
const_tree, bool);
static void avr_help (void);
+static bool avr_function_ok_for_sibcall (tree, tree);
/* Allocate registers from r25 to r8 for parameters for function calls. */
#define FIRST_CUM_REG 26
#undef TARGET_EXCEPT_UNWIND_INFO
#define TARGET_EXCEPT_UNWIND_INFO sjlj_except_unwind_info
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL avr_function_ok_for_sibcall
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
static void
return ALL_REGS;
}
+/* A helper for the subsequent function attribute used to dig for
+ attribute 'name' in a FUNCTION_DECL or FUNCTION_TYPE */
+
+static inline int
+avr_lookup_function_attribute1 (const_tree func, const char *name)
+{
+ if (FUNCTION_DECL == TREE_CODE (func))
+ {
+ if (NULL_TREE != lookup_attribute (name, DECL_ATTRIBUTES (func)))
+ {
+ return true;
+ }
+
+ func = TREE_TYPE (func);
+ }
+
+ gcc_assert (TREE_CODE (func) == FUNCTION_TYPE
+ || TREE_CODE (func) == METHOD_TYPE);
+
+ return NULL_TREE != lookup_attribute (name, TYPE_ATTRIBUTES (func));
+}
+
/* Return nonzero if FUNC is a naked function. */
static int
avr_naked_function_p (tree func)
{
- tree a;
-
- gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-
- a = lookup_attribute ("naked", TYPE_ATTRIBUTES (TREE_TYPE (func)));
- return a != NULL_TREE;
+ return avr_lookup_function_attribute1 (func, "naked");
}
/* Return nonzero if FUNC is an interrupt function as specified
static int
interrupt_function_p (tree func)
{
- tree a;
-
- if (TREE_CODE (func) != FUNCTION_DECL)
- return 0;
-
- a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func));
- return a != NULL_TREE;
+ return avr_lookup_function_attribute1 (func, "interrupt");
}
/* Return nonzero if FUNC is a signal function as specified
static int
signal_function_p (tree func)
{
- tree a;
-
- if (TREE_CODE (func) != FUNCTION_DECL)
- return 0;
-
- a = lookup_attribute ("signal", DECL_ATTRIBUTES (func));
- return a != NULL_TREE;
+ return avr_lookup_function_attribute1 (func, "signal");
}
/* Return nonzero if FUNC is a OS_task function. */
static int
avr_OS_task_function_p (tree func)
{
- tree a;
-
- gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-
- a = lookup_attribute ("OS_task", TYPE_ATTRIBUTES (TREE_TYPE (func)));
- return a != NULL_TREE;
+ return avr_lookup_function_attribute1 (func, "OS_task");
}
/* Return nonzero if FUNC is a OS_main function. */
static int
avr_OS_main_function_p (tree func)
{
- tree a;
-
- gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
-
- a = lookup_attribute ("OS_main", TYPE_ATTRIBUTES (TREE_TYPE (func)));
- return a != NULL_TREE;
+ return avr_lookup_function_attribute1 (func, "OS_main");
}
/* Return the number of hard registers to push/pop in the prologue/epilogue
/* Output RTL epilogue. */
void
-expand_epilogue (void)
+expand_epilogue (bool sibcall_p)
{
int reg;
int live_seq;
/* epilogue: naked */
if (cfun->machine->is_naked)
{
+ gcc_assert (!sibcall_p);
+
emit_jump_insn (gen_return ());
return;
}
emit_pop_byte (ZERO_REGNO);
}
- emit_jump_insn (gen_return ());
+ if (!sibcall_p)
+ emit_jump_insn (gen_return ());
}
}
cum->regno = FIRST_CUM_REG;
if (!libname && stdarg_p (fntype))
cum->nregs = 0;
+
+ /* Assume the calle may be tail called */
+
+ cfun->machine->sibcall_fails = 0;
}
/* Returns the number of registers to allocate for a function argument. */
cum->nregs -= bytes;
cum->regno -= bytes;
+ /* A parameter is being passed in a call-saved register. As the original
+ contents of these regs has to be restored before leaving the function,
+ a function must not pass arguments in call-saved regs in order to get
+ tail-called. */
+
+ if (cum->regno >= 0
+ && !call_used_regs[cum->regno])
+ {
+ /* FIXME: We ship info on failing tail-call in struct machine_function.
+ This uses internals of calls.c:expand_call() and the way args_so_far
+ is used. targetm.function_ok_for_sibcall() needs to be extended to
+ pass &args_so_far, too. At present, CUMULATIVE_ARGS is target
+ dependent so that such an extension is not wanted. */
+
+ cfun->machine->sibcall_fails = 1;
+ }
+
if (cum->nregs <= 0)
{
cum->nregs = 0;
}
}
+/* Implement `TARGET_FUNCTION_OK_FOR_SIBCALL' */
+/* Decide whether we can make a sibling call to a function. DECL is the
+ declaration of the function being targeted by the call and EXP is the
+ CALL_EXPR representing the call. */
+
+static bool
+avr_function_ok_for_sibcall (tree decl_callee, tree exp_callee)
+{
+ tree fntype_callee;
+
+ /* Tail-calling must fail if callee-saved regs are used to pass
+ function args. We must not tail-call when `epilogue_restores'
+ is used. Unfortunately, we cannot tell at this point if that
+ actually will happen or not, and we cannot step back from
+ tail-calling. Thus, we inhibit tail-calling with -mcall-prologues. */
+
+ if (cfun->machine->sibcall_fails
+ || TARGET_CALL_PROLOGUES)
+ {
+ return false;
+ }
+
+ fntype_callee = TREE_TYPE (CALL_EXPR_FN (exp_callee));
+
+ if (decl_callee)
+ {
+ decl_callee = TREE_TYPE (decl_callee);
+ }
+ else
+ {
+ decl_callee = fntype_callee;
+
+ while (FUNCTION_TYPE != TREE_CODE (decl_callee)
+ && METHOD_TYPE != TREE_CODE (decl_callee))
+ {
+ decl_callee = TREE_TYPE (decl_callee);
+ }
+ }
+
+ /* Ensure that caller and callee have compatible epilogues */
+
+ if (interrupt_function_p (current_function_decl)
+ || signal_function_p (current_function_decl)
+ || avr_naked_function_p (decl_callee)
+ || avr_naked_function_p (current_function_decl)
+ /* FIXME: For OS_task and OS_main, we are over-conservative.
+ This is due to missing documentation of these attributes
+ and what they actually should do and should not do. */
+ || (avr_OS_task_function_p (decl_callee)
+ != avr_OS_task_function_p (current_function_decl))
+ || (avr_OS_main_function_p (decl_callee)
+ != avr_OS_main_function_p (current_function_decl)))
+ {
+ return false;
+ }
+
+ return true;
+}
+
/***********************************************************************
Functions for outputting various mov's for a various modes
************************************************************************/
/* Current function stack size. */
int stack_usage;
+
+ /* 'true' if a callee might be tail called */
+ int sibcall_fails;
};
;; call
(define_expand "call"
- [(call (match_operand:HI 0 "call_insn_operand" "")
- (match_operand:HI 1 "general_operand" ""))]
+ [(parallel[(call (match_operand:HI 0 "call_insn_operand" "")
+ (match_operand:HI 1 "general_operand" ""))
+ (use (const_int 0))])]
;; Operand 1 not used on the AVR.
+ ;; Operand 2 is 1 for tail-call, 0 otherwise.
+ ""
+ "")
+
+(define_expand "sibcall"
+ [(parallel[(call (match_operand:HI 0 "call_insn_operand" "")
+ (match_operand:HI 1 "general_operand" ""))
+ (use (const_int 1))])]
+ ;; Operand 1 not used on the AVR.
+ ;; Operand 2 is 1 for tail-call, 0 otherwise.
""
"")
;; call value
(define_expand "call_value"
- [(set (match_operand 0 "register_operand" "")
- (call (match_operand:HI 1 "call_insn_operand" "")
- (match_operand:HI 2 "general_operand" "")))]
+ [(parallel[(set (match_operand 0 "register_operand" "")
+ (call (match_operand:HI 1 "call_insn_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (use (const_int 0))])]
+ ;; Operand 2 not used on the AVR.
+ ;; Operand 3 is 1 for tail-call, 0 otherwise.
+ ""
+ "")
+
+(define_expand "sibcall_value"
+ [(parallel[(set (match_operand 0 "register_operand" "")
+ (call (match_operand:HI 1 "call_insn_operand" "")
+ (match_operand:HI 2 "general_operand" "")))
+ (use (const_int 1))])]
;; Operand 2 not used on the AVR.
+ ;; Operand 3 is 1 for tail-call, 0 otherwise.
""
"")
-(define_insn "call_insn"
- [(call (mem:HI (match_operand:HI 0 "nonmemory_operand" "!z,*r,s,n"))
- (match_operand:HI 1 "general_operand" "X,X,X,X"))]
-;; We don't need in saving Z register because r30,r31 is a call used registers
+(define_insn "*call_insn"
+ [(parallel[(call (mem:HI (match_operand:HI 0 "nonmemory_operand" "z,s,z,s"))
+ (match_operand:HI 1 "general_operand" "X,X,X,X"))
+ (use (match_operand:HI 2 "const_int_operand" "L,L,P,P"))])]
;; Operand 1 not used on the AVR.
- "(register_operand (operands[0], HImode) || CONSTANT_P (operands[0]))"
- "*{
- if (which_alternative==0)
- return \"%!icall\";
- else if (which_alternative==1)
- {
- if (AVR_HAVE_MOVW)
- return (AS2 (movw, r30, %0) CR_TAB
- \"%!icall\");
- else
- return (AS2 (mov, r30, %A0) CR_TAB
- AS2 (mov, r31, %B0) CR_TAB
- \"%!icall\");
- }
- else if (which_alternative==2)
- return AS1(%~call,%x0);
- return (AS2 (ldi,r30,lo8(%0)) CR_TAB
- AS2 (ldi,r31,hi8(%0)) CR_TAB
- \"%!icall\");
-}"
- [(set_attr "cc" "clobber,clobber,clobber,clobber")
+ ;; Operand 2 is 1 for tail-call, 0 otherwise.
+ ""
+ "@
+ %!icall
+ %~call %x0
+ %!ijmp
+ %~jmp %x0"
+ [(set_attr "cc" "clobber")
(set_attr_alternative "length"
- [(const_int 1)
- (if_then_else (eq_attr "mcu_have_movw" "yes")
- (const_int 2)
- (const_int 3))
- (if_then_else (eq_attr "mcu_mega" "yes")
- (const_int 2)
- (const_int 1))
- (const_int 3)])])
-
-(define_insn "call_value_insn"
- [(set (match_operand 0 "register_operand" "=r,r,r,r")
- (call (mem:HI (match_operand:HI 1 "nonmemory_operand" "!z,*r,s,n"))
-;; We don't need in saving Z register because r30,r31 is a call used registers
- (match_operand:HI 2 "general_operand" "X,X,X,X")))]
+ [(const_int 1)
+ (if_then_else (eq_attr "mcu_mega" "yes")
+ (const_int 2)
+ (const_int 1))
+ (const_int 1)
+ (if_then_else (eq_attr "mcu_mega" "yes")
+ (const_int 2)
+ (const_int 1))])])
+
+(define_insn "*call_value_insn"
+ [(parallel[(set (match_operand 0 "register_operand" "=r,r,r,r")
+ (call (mem:HI (match_operand:HI 1 "nonmemory_operand" "z,s,z,s"))
+ (match_operand:HI 2 "general_operand" "X,X,X,X")))
+ (use (match_operand:HI 3 "const_int_operand" "L,L,P,P"))])]
;; Operand 2 not used on the AVR.
- "(register_operand (operands[0], VOIDmode) || CONSTANT_P (operands[0]))"
- "*{
- if (which_alternative==0)
- return \"%!icall\";
- else if (which_alternative==1)
- {
- if (AVR_HAVE_MOVW)
- return (AS2 (movw, r30, %1) CR_TAB
- \"%!icall\");
- else
- return (AS2 (mov, r30, %A1) CR_TAB
- AS2 (mov, r31, %B1) CR_TAB
- \"%!icall\");
- }
- else if (which_alternative==2)
- return AS1(%~call,%x1);
- return (AS2 (ldi, r30, lo8(%1)) CR_TAB
- AS2 (ldi, r31, hi8(%1)) CR_TAB
- \"%!icall\");
-}"
- [(set_attr "cc" "clobber,clobber,clobber,clobber")
+ ;; Operand 3 is 1 for tail-call, 0 otherwise.
+ ""
+ "@
+ %!icall
+ %~call %x1
+ %!ijmp
+ %~jmp %x1"
+ [(set_attr "cc" "clobber")
(set_attr_alternative "length"
- [(const_int 1)
- (if_then_else (eq_attr "mcu_have_movw" "yes")
- (const_int 2)
- (const_int 3))
- (if_then_else (eq_attr "mcu_mega" "yes")
- (const_int 2)
- (const_int 1))
- (const_int 3)])])
+ [(const_int 1)
+ (if_then_else (eq_attr "mcu_mega" "yes")
+ (const_int 2)
+ (const_int 1))
+ (const_int 1)
+ (if_then_else (eq_attr "mcu_mega" "yes")
+ (const_int 2)
+ (const_int 1))])])
(define_insn "nop"
[(const_int 0)]
(define_expand "epilogue"
[(const_int 0)]
""
- "
{
- expand_epilogue ();
+ expand_epilogue (false /* sibcall_p */);
DONE;
- }")
+ })
+
+(define_expand "sibcall_epilogue"
+ [(const_int 0)]
+ ""
+ {
+ expand_epilogue (true /* sibcall_p */);
+ DONE;
+ })