/* Convert function calls to rtl insns, for GNU C compiler.
Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
- 1999, 2000 Free Software Foundation, Inc.
+ 1999, 2000, 2001 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "expr.h"
#include "function.h"
#include "regs.h"
-#include "insn-flags.h"
#include "toplev.h"
#include "output.h"
#include "tm_p.h"
#include "timevar.h"
#include "sbitmap.h"
-#ifndef ACCUMULATE_OUTGOING_ARGS
-#define ACCUMULATE_OUTGOING_ARGS 0
-#endif
-
-/* Supply a default definition for PUSH_ARGS. */
-#ifndef PUSH_ARGS
-#ifdef PUSH_ROUNDING
-#define PUSH_ARGS !ACCUMULATE_OUTGOING_ARGS
-#else
-#define PUSH_ARGS 0
-#endif
-#endif
-
#if !defined FUNCTION_OK_FOR_SIBCALL
#define FUNCTION_OK_FOR_SIBCALL(DECL) 1
#endif
#define PUSH_ARGS_REVERSED 0
#endif
+#ifndef STACK_POINTER_OFFSET
+#define STACK_POINTER_OFFSET 0
+#endif
+
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
static rtx rtx_for_function_call PARAMS ((tree, tree));
static void load_register_parameters PARAMS ((struct arg_data *,
int, rtx *, int));
-static int libfunc_nothrow PARAMS ((rtx));
static rtx emit_library_call_value_1 PARAMS ((int, rtx, rtx,
enum libcall_type,
enum machine_mode,
break;
+ case CONSTRUCTOR:
+ {
+ tree tem;
+
+ for (tem = CONSTRUCTOR_ELTS (exp); tem != 0; tem = TREE_CHAIN (tem))
+ if (calls_function_1 (TREE_VALUE (tem), which))
+ return 1;
+ }
+
+ return 0;
+
case SAVE_EXPR:
if (SAVE_EXPR_RTL (exp) != 0)
return 0;
if ((flags & (ECF_CONST | ECF_PURE))
|| calls_function (args[i].tree_value, !ACCUMULATE_OUTGOING_ARGS))
{
+ enum machine_mode mode;
+
/* If this is an addressable type, we cannot pre-evaluate it. */
if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))
abort ();
args[i].initial_value = args[i].value
= protect_from_queue (args[i].value, 0);
- if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode)
+ mode = TYPE_MODE (TREE_TYPE (args[i].tree_value));
+ if (mode != args[i].mode)
{
args[i].value
- = convert_modes (args[i].mode,
- TYPE_MODE (TREE_TYPE (args[i].tree_value)),
+ = convert_modes (args[i].mode, mode,
args[i].value, args[i].unsignedp);
#ifdef PROMOTE_FOR_CALL_ONLY
/* CSE will replace this only if it contains args[i].value
&& GET_MODE_CLASS (args[i].mode) == MODE_INT)
{
args[i].initial_value
- = gen_rtx_SUBREG (TYPE_MODE (TREE_TYPE (args[i].tree_value)),
- args[i].value, 0);
+ = gen_lowpart_SUBREG (mode, args[i].value);
SUBREG_PROMOTED_VAR_P (args[i].initial_value) = 1;
SUBREG_PROMOTED_UNSIGNED_P (args[i].initial_value)
= args[i].unsignedp;
args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data));
memset ((char *) args, 0, num_actuals * sizeof (struct arg_data));
- /* Build up entries inthe ARGS array, compute the size of the arguments
- into ARGS_SIZE, etc. */
+ /* Build up entries in the ARGS array, compute the size of the
+ arguments into ARGS_SIZE, etc. */
initialize_argument_information (num_actuals, args, &args_size,
n_named_args, actparms, fndecl,
&args_so_far, reg_parm_stack_space,
{
tree var = build_decl (VAR_DECL, NULL_TREE,
TREE_TYPE (args[i].tree_value));
- DECL_RTL (var) = expand_expr (args[i].tree_value, NULL_RTX,
- VOIDmode, EXPAND_NORMAL);
+ SET_DECL_RTL (var,
+ expand_expr (args[i].tree_value, NULL_RTX,
+ VOIDmode, EXPAND_NORMAL));
args[i].tree_value = var;
}
break;
{
tree type = TREE_TYPE (exp);
int unsignedp = TREE_UNSIGNED (type);
+ int offset = 0;
/* If we don't promote as expected, something is wrong. */
if (GET_MODE (target)
!= promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
abort ();
- target = gen_rtx_SUBREG (TYPE_MODE (type), target, 0);
+ if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
+ && GET_MODE_SIZE (GET_MODE (target))
+ > GET_MODE_SIZE (TYPE_MODE (type)))
+ {
+ offset = GET_MODE_SIZE (GET_MODE (target))
+ - GET_MODE_SIZE (TYPE_MODE (type));
+ if (! BYTES_BIG_ENDIAN)
+ offset = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
+ else if (! WORDS_BIG_ENDIAN)
+ offset %= UNITS_PER_WORD;
+ }
+ target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
SUBREG_PROMOTED_VAR_P (target) = 1;
SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
}
return target;
}
\f
-/* Returns nonzero if FUN is the symbol for a library function which can
- not throw. */
-
-static int
-libfunc_nothrow (fun)
- rtx fun;
-{
- if (fun == throw_libfunc
- || fun == rethrow_libfunc
- || fun == sjthrow_libfunc
- || fun == sjpopnthrow_libfunc)
- return 0;
-
- return 1;
-}
-\f
/* Output a library call to function FUN (a SYMBOL_REF rtx).
The RETVAL parameter specifies whether return value needs to be saved, other
parameters are documented in the emit_library_call function bellow. */
rtx valreg;
int pcc_struct_value = 0;
int struct_value_size = 0;
- int flags = 0;
+ int flags;
int reg_parm_stack_space = 0;
int needed;
+ rtx before_call;
#ifdef REG_PARM_STACK_SPACE
/* Define the boundary of the register parm stack space that needs to be
#endif
#endif
- if (fn_type == LCT_CONST_MAKE_BLOCK)
- flags |= ECF_CONST;
- else if (fn_type == LCT_PURE_MAKE_BLOCK)
- flags |= ECF_PURE;
- fun = orgfun;
+ /* By default, library functions can not throw. */
+ flags = ECF_NOTHROW;
- if (libfunc_nothrow (fun))
- flags |= ECF_NOTHROW;
+ switch (fn_type)
+ {
+ case LCT_NORMAL:
+ case LCT_CONST:
+ case LCT_PURE:
+ /* Nothing to do here. */
+ break;
+ case LCT_CONST_MAKE_BLOCK:
+ flags |= ECF_CONST;
+ break;
+ case LCT_PURE_MAKE_BLOCK:
+ flags |= ECF_PURE;
+ break;
+ case LCT_NORETURN:
+ flags |= ECF_NORETURN;
+ break;
+ case LCT_THROW:
+ flags = ECF_NORETURN;
+ break;
+ }
+ fun = orgfun;
#ifdef PREFERRED_STACK_BOUNDARY
/* Ensure current function's preferred stack boundary is at least
argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg));
memset ((char *) argvec, 0, (nargs + 1) * sizeof (struct arg));
+#ifdef INIT_CUMULATIVE_LIBCALL_ARGS
+ INIT_CUMULATIVE_LIBCALL_ARGS (args_so_far, outmode, fun);
+#else
INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
+#endif
args_size.constant = 0;
args_size.var = 0;
#ifdef FUNCTION_ARG_PASS_BY_REFERENCE
if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1))
{
- /* We do not support FUNCTION_ARG_CALLEE_COPIES here since it can
- be viewed as just an efficiency improvement. */
- rtx slot = assign_temp (type_for_mode (mode, 0), 0, 1, 1);
+ rtx slot;
+ int must_copy = 1
+#ifdef FUNCTION_ARG_CALLEE_COPIES
+ && ! FUNCTION_ARG_CALLEE_COPIES (args_so_far, mode,
+ NULL_TREE, 1)
+#endif
+ ;
+
+ if (GET_MODE (val) == MEM && ! must_copy)
+ slot = val;
+ else if (must_copy)
+ {
+ slot = assign_temp (type_for_mode (mode, 0), 0, 1, 1);
+ emit_move_insn (slot, val);
+ }
+ else
+ {
+ tree type = 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));
+ }
call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_USE (VOIDmode, slot),
call_fusage);
- emit_move_insn (slot, val);
- val = force_operand (XEXP (slot, 0), NULL_RTX);
+ if (must_copy)
+ call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_CLOBBER (VOIDmode,
+ slot),
+ call_fusage);
+
mode = Pmode;
+ val = force_operand (XEXP (slot, 0), NULL_RTX);
}
#endif
highest_outgoing_arg_in_use - initial_highest_arg_in_use);
needed = 0;
- /* The address of the outgoing argument list must not be copied to a
- register here, because argblock would be left pointing to the
- wrong place after the call to allocate_dynamic_stack_space below. */
+ /* We must be careful to use virtual regs before they're instantiated,
+ and real regs afterwards. Loop optimization, for example, can create
+ new libcalls after we've instantiated the virtual regs, and if we
+ use virtuals anyway, they won't match the rtl patterns. */
- argblock = virtual_outgoing_args_rtx;
+ if (virtuals_instantiated)
+ argblock = plus_constant (stack_pointer_rtx, STACK_POINTER_OFFSET);
+ else
+ argblock = virtual_outgoing_args_rtx;
}
else
{
abort ();
#endif
+ before_call = get_last_insn ();
+
/* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
will set inhibit_defer_pop to that value. */
/* The return type is needed to decide how many bytes the function pops.
valreg,
old_inhibit_defer_pop + 1, call_fusage, flags);
+ /* For calls to `setjmp', etc., inform flow.c it should complain
+ if nonvolatile values are live. For functions that cannot return,
+ inform flow that control does not fall through. */
+
+ if (flags & (ECF_RETURNS_TWICE | ECF_NORETURN | ECF_LONGJMP))
+ {
+ /* The barrier or NOTE_INSN_SETJMP note must be emitted
+ immediately after the CALL_INSN. Some ports emit more than
+ just a CALL_INSN above, so we must search for it here. */
+
+ rtx last = get_last_insn ();
+ while (GET_CODE (last) != CALL_INSN)
+ {
+ last = PREV_INSN (last);
+ /* There was no CALL_INSN? */
+ if (last == before_call)
+ abort ();
+ }
+
+ if (flags & ECF_RETURNS_TWICE)
+ {
+ emit_note_after (NOTE_INSN_SETJMP, last);
+ current_function_calls_setjmp = 1;
+ }
+ else
+ emit_barrier_after (last);
+ }
+
/* Now restore inhibit_defer_pop to its actual original value. */
OK_DEFER_POP;
{
rtx size_rtx1 = GEN_INT (reg_parm_stack_space - arg->offset.constant);
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx1,
- TYPE_ALIGN (TREE_TYPE (pval)) / BITS_PER_UNIT,
- partial, reg, excess, argblock,
- ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space,
+ TYPE_ALIGN (TREE_TYPE (pval)), partial, reg,
+ excess, argblock, ARGS_SIZE_RTX (arg->offset),
+ reg_parm_stack_space,
ARGS_SIZE_RTX (arg->alignment_pad));
size_rtx = GEN_INT (INTVAL(size_rtx) - reg_parm_stack_space);