#include "sbitmap.h"
#include "langhooks.h"
#include "target.h"
+#include "cgraph.h"
/* Decide whether a function's arguments should be processed
from first to last or from last to first.
/* ??? We can't set IS_MALLOC for function types? */
if (DECL_P (exp))
{
+ struct cgraph_rtl_info *i = cgraph_rtl_info (exp);
type = TREE_TYPE (exp);
+ if (i && i->pure_function)
+ flags |= ECF_PURE | ECF_LIBCALL_BLOCK;
+ if (i && i->const_function)
+ flags |= ECF_CONST | ECF_LIBCALL_BLOCK;
+
/* The function exp may have the `malloc' attribute. */
if (DECL_P (exp) && DECL_IS_MALLOC (exp))
flags |= ECF_MALLOC;
addr = plus_constant (addr, arg_offset);
args[i].stack = gen_rtx_MEM (args[i].mode, addr);
+ set_mem_align (args[i].stack, PARM_BOUNDARY);
set_mem_attributes (args[i].stack,
TREE_TYPE (args[i].tree_value), 1);
addr = plus_constant (addr, arg_offset);
args[i].stack_slot = gen_rtx_MEM (args[i].mode, addr);
+ set_mem_align (args[i].stack_slot, PARM_BOUNDARY);
set_mem_attributes (args[i].stack_slot,
TREE_TYPE (args[i].tree_value), 1);
FNDECL is the tree node for the target function. For an indirect call
FNDECL will be NULL_TREE.
- EXP is the CALL_EXPR for this call. */
+ ADDR is the operand 0 of CALL_EXPR for this call. */
static rtx
-rtx_for_function_call (fndecl, exp)
+rtx_for_function_call (fndecl, addr)
tree fndecl;
- tree exp;
+ tree addr;
{
rtx funexp;
/* Generate an rtx (probably a pseudo-register) for the address. */
{
push_temp_slots ();
- funexp = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+ funexp = expand_expr (addr, NULL_RTX, VOIDmode, 0);
pop_temp_slots (); /* FUNEXP can't be BLKmode. */
emit_queue ();
}
the stack before executing the inlined function if it
makes any calls. */
- for (i = reg_parm_stack_space - 1; i >= 0; i--)
- if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
- break;
+ i = reg_parm_stack_space;
+ if (i > highest_outgoing_arg_in_use)
+ i = highest_outgoing_arg_in_use;
+ while (--i >= 0 && stack_usage_map[i] == 0)
+ ;
if (stack_arg_under_construction || i >= 0)
{
int old_stack_allocated;
rtx call_fusage;
tree p = TREE_OPERAND (exp, 0);
+ tree addr = TREE_OPERAND (exp, 0);
int i;
/* The alignment of the stack, in bits. */
HOST_WIDE_INT preferred_stack_boundary;
/* Figure out the amount to which the stack should be aligned. */
preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
+ if (fndecl)
+ {
+ struct cgraph_rtl_info *i = cgraph_rtl_info (fndecl);
+ if (i && i->preferred_incoming_stack_boundary)
+ preferred_stack_boundary = i->preferred_incoming_stack_boundary;
+ }
/* Operand 0 is a pointer-to-function; get the type of the function. */
- funtype = TREE_TYPE (TREE_OPERAND (exp, 0));
+ funtype = TREE_TYPE (addr);
if (! POINTER_TYPE_P (funtype))
abort ();
funtype = TREE_TYPE (funtype);
calling convention than normal calls. The last argument in
INIT_CUMULATIVE_ARGS tells the backend if this is an indirect call
or not. */
- INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, (fndecl == 0));
+ INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_RTX, fndecl);
/* Make a vector to hold all the information about each arg. */
args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
/* Tail recursion fails, when we are not dealing with recursive calls. */
if (!try_tail_recursion
- || TREE_CODE (TREE_OPERAND (exp, 0)) != ADDR_EXPR
- || TREE_OPERAND (TREE_OPERAND (exp, 0), 0) != current_function_decl)
+ || TREE_CODE (addr) != ADDR_EXPR
+ || TREE_OPERAND (addr, 0) != current_function_decl)
try_tail_recursion = 0;
/* Rest of purposes for tail call optimizations to fail. */
/* Functions that do not return exactly once may not be sibcall
optimized. */
|| (flags & (ECF_RETURNS_TWICE | ECF_LONGJMP | ECF_NORETURN))
- || TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+ || TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr)))
/* If this function requires more stack slots than the current
function, we cannot change it into a sibling call. */
|| args_size.constant > current_function_args_size
if (try_tail_recursion)
actparms = tree_cons (NULL_TREE, args[i].tree_value, actparms);
}
- /* Do the same for the function address if it is an expression. */
+ /* Do the same for the function address if it is an expression. */
if (!fndecl)
- TREE_OPERAND (exp, 0) = fix_unsafe_tree (TREE_OPERAND (exp, 0));
+ addr = fix_unsafe_tree (addr);
/* Expanding one of those dangerous arguments could have added
cleanups, but otherwise give it a whirl. */
if (any_pending_cleanups (1))
if (cfun->preferred_stack_boundary < preferred_stack_boundary
&& fndecl != current_function_decl)
cfun->preferred_stack_boundary = preferred_stack_boundary;
+ if (fndecl == current_function_decl)
+ cfun->recursive_call_emit = true;
preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
be deferred during the evaluation of the arguments. */
NO_DEFER_POP;
- funexp = rtx_for_function_call (fndecl, exp);
+ funexp = rtx_for_function_call (fndecl, addr);
/* Figure out the register where the value, if any, will come back. */
valreg = 0;
{
tree type = (*lang_hooks.types.type_for_mode) (mode, 0);
- slot = gen_rtx_MEM (mode,
- expand_expr (build1 (ADDR_EXPR,
- build_pointer_type
- (type),
- make_tree (type, val)),
- NULL_RTX, VOIDmode, 0));
+ slot
+ = gen_rtx_MEM (mode,
+ expand_expr (build1 (ADDR_EXPR,
+ build_pointer_type (type),
+ make_tree (type, val)),
+ NULL_RTX, VOIDmode, 0));
}
call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
upper_bound = lower_bound + argvec[argnum].size.constant;
#endif
- for (i = lower_bound; i < upper_bound; i++)
- if (stack_usage_map[i]
- /* Don't store things in the fixed argument area at this
- point; it has already been saved. */
- && i > reg_parm_stack_space)
- break;
+ i = lower_bound;
+ /* Don't worry about things in the fixed argument area;
+ it has already been saved. */
+ if (i < reg_parm_stack_space)
+ i = reg_parm_stack_space;
+ while (i < upper_bound && stack_usage_map[i] == 0)
+ i++;
- if (i != upper_bound)
+ if (i < upper_bound)
{
/* We need to make a save area. See what mode we can make
it. */
upper_bound = lower_bound + arg->size.constant;
#endif
- for (i = lower_bound; i < upper_bound; i++)
- if (stack_usage_map[i]
- /* Don't store things in the fixed argument area at this point;
- it has already been saved. */
- && i > reg_parm_stack_space)
- break;
+ i = lower_bound;
+ /* Don't worry about things in the fixed argument area;
+ it has already been saved. */
+ if (i < reg_parm_stack_space)
+ i = reg_parm_stack_space;
+ while (i < upper_bound && stack_usage_map[i] == 0)
+ i++;
- if (i != upper_bound)
+ if (i < upper_bound)
{
/* We need to make a save area. See what mode we can make it. */
enum machine_mode save_mode
}
}
}
- /* Now that we have saved any slots that will be overwritten by this
- store, mark all slots this store will use. We must do this before
- we actually expand the argument since the expansion itself may
- trigger library calls which might need to use the same stack slot. */
- if (argblock && ! variable_size && arg->stack)
- for (i = lower_bound; i < upper_bound; i++)
- stack_usage_map[i] = 1;
}
/* If this isn't going to be placed on both the stack and in registers,
(partial
|| TYPE_MODE (TREE_TYPE (pval)) != arg->mode)
? NULL_RTX : arg->stack,
- VOIDmode, 0);
+ VOIDmode, EXPAND_STACK_PARM);
/* If we are promoting object (or for any other reason) the mode
doesn't agree, convert the mode. */
}
}
- /* Special handling is required if part of the parameter lies in the
- register parameter area. The argument may be copied into the stack
- slot using memcpy(), but the original contents of the register
- parameter area will be restored after the memcpy() call.
-
- To ensure that the part that lies in the register parameter area
- is copied correctly, we emit a separate push for that part. This
- push should be small enough to avoid a call to memcpy(). */
-#ifndef STACK_PARMS_IN_REG_PARM_AREA
- if (arg->reg && arg->pass_on_stack)
-#else
- if (1)
-#endif
- {
- if (arg->offset.constant < reg_parm_stack_space && arg->offset.var)
- error ("variable offset is passed partially in stack and in reg");
- else if (arg->offset.constant < reg_parm_stack_space && arg->size.var)
- error ("variable size is passed partially in stack and in reg");
- else if (arg->offset.constant < reg_parm_stack_space
- && ((arg->offset.constant + arg->size.constant)
- > reg_parm_stack_space))
- {
- rtx size_rtx1 = GEN_INT (reg_parm_stack_space - arg->offset.constant);
- emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx1,
- parm_align, partial, reg, excess, argblock,
- ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
- ARGS_SIZE_RTX (arg->alignment_pad));
- }
- }
-
-
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
parm_align, partial, reg, excess, argblock,
ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
arg->value = arg->stack_slot;
}
+ /* Mark all slots this store used. */
+ if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL)
+ && argblock && ! variable_size && arg->stack)
+ for (i = lower_bound; i < upper_bound; i++)
+ stack_usage_map[i] = 1;
+
/* Once we have pushed something, pops can't safely
be deferred during the rest of the arguments. */
NO_DEFER_POP;
return sibcall_failure;
}
-
/* Nonzero if we do not know how to pass TYPE solely in registers.
We cannot do so in the following cases:
tree type;
{
if (!type)
- return true;
+ return false;
/* If the type has variable size... */
if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)