From: Richard Kenner Date: Tue, 23 Mar 1993 13:09:36 +0000 (-0500) Subject: Include gvarargs.h. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=322e3e342039ed1f1aa8a53cd3948ed9313b87c5;p=gcc.git Include gvarargs.h. Add prototypes to forward declarations and declare static functions. (prepare_call_address, emit_call_1): Now static. (emit_library_call, emit_library_call_value): Move to here. From-SVN: r3848 --- diff --git a/gcc/calls.c b/gcc/calls.c index a4b8747a02b..2b024d45ece 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -22,6 +22,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "tree.h" #include "flags.h" #include "expr.h" +#include "gvarargs.h" #include "insn-flags.h" /* Decide whether a function's arguments should be processed @@ -117,8 +118,12 @@ static int highest_outgoing_arg_in_use; int stack_arg_under_construction; #endif -static void store_one_arg (); -extern enum machine_mode mode_for_size (); +static int calls_function PROTO((tree, int)); +static rtx prepare_call_address PROTO((rtx, tree, rtx *)); +static void emit_call_1 PROTO((rtx, tree, int, int, rtx, rtx, int, + rtx, int)); +static void store_one_arg PROTO ((struct arg_data *, rtx, int, int, + tree, int)); /* If WHICH is 1, return 1 if EXP contains a call to the built-in function `alloca'. @@ -213,7 +218,7 @@ calls_function (exp, which) to which a USE of the static chain register should be added, if required. */ -rtx +static rtx prepare_call_address (funexp, fndecl, use_insns) rtx funexp; tree fndecl; @@ -293,7 +298,7 @@ prepare_call_address (funexp, fndecl, use_insns) IS_CONST is true if this is a `const' call. */ -void +static void emit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg, valreg, old_inhibit_defer_pop, use_insns, is_const) rtx funexp; @@ -1931,6 +1936,604 @@ expand_call (exp, target, ignore) return target; } +/* Output a library call to function FUN (a SYMBOL_REF rtx) + (emitting the queue unless NO_QUEUE is nonzero), + for a value of mode OUTMODE, + with NARGS different arguments, passed as alternating rtx values + and machine_modes to convert them to. + The rtx values should have been passed through protect_from_queue already. + + NO_QUEUE will be true if and only if the library call is a `const' call + which will be enclosed in REG_LIBCALL/REG_RETVAL notes; it is equivalent + to the variable is_const in expand_call. + + NO_QUEUE must be true for const calls, because if it isn't, then + any pending increment will be emitted between REG_LIBCALL/REG_RETVAL notes, + and will be lost if the libcall sequence is optimized away. + + NO_QUEUE must be false for non-const calls, because if it isn't, the + call insn will have its CONST_CALL_P bit set, and it will be incorrectly + optimized. For instance, the instruction scheduler may incorrectly + move memory references across the non-const call. */ + +void +emit_library_call (va_alist) + va_dcl +{ + va_list p; + /* Total size in bytes of all the stack-parms scanned so far. */ + struct args_size args_size; + /* Size of arguments before any adjustments (such as rounding). */ + struct args_size original_args_size; + register int argnum; + enum machine_mode outmode; + int nargs; + rtx fun; + rtx orgfun; + int inc; + int count; + rtx argblock = 0; + CUMULATIVE_ARGS args_so_far; + struct arg { rtx value; enum machine_mode mode; rtx reg; int partial; + struct args_size offset; struct args_size size; }; + struct arg *argvec; + int old_inhibit_defer_pop = inhibit_defer_pop; + int no_queue = 0; + rtx use_insns; + + va_start (p); + orgfun = fun = va_arg (p, rtx); + no_queue = va_arg (p, int); + outmode = va_arg (p, enum machine_mode); + nargs = va_arg (p, int); + + /* Copy all the libcall-arguments out of the varargs data + and into a vector ARGVEC. + + Compute how to pass each argument. We only support a very small subset + of the full argument passing conventions to limit complexity here since + library functions shouldn't have many args. */ + + argvec = (struct arg *) alloca (nargs * sizeof (struct arg)); + + INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun); + + args_size.constant = 0; + args_size.var = 0; + + for (count = 0; count < nargs; count++) + { + rtx val = va_arg (p, rtx); + enum machine_mode mode = va_arg (p, enum machine_mode); + + /* We cannot convert the arg value to the mode the library wants here; + must do it earlier where we know the signedness of the arg. */ + if (mode == BLKmode + || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)) + abort (); + + /* On some machines, there's no way to pass a float to a library fcn. + Pass it as a double instead. */ +#ifdef LIBGCC_NEEDS_DOUBLE + if (LIBGCC_NEEDS_DOUBLE && mode == SFmode) + val = convert_to_mode (DFmode, val, 0), mode = DFmode; +#endif + + /* There's no need to call protect_from_queue, because + either emit_move_insn or emit_push_insn will do that. */ + + /* Make sure it is a reasonable operand for a move or push insn. */ + if (GET_CODE (val) != REG && GET_CODE (val) != MEM + && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val))) + val = force_operand (val, NULL_RTX); + + argvec[count].value = val; + argvec[count].mode = mode; + +#ifdef FUNCTION_ARG_PASS_BY_REFERENCE + if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1)) + abort (); +#endif + + argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1); + if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST) + abort (); +#ifdef FUNCTION_ARG_PARTIAL_NREGS + argvec[count].partial + = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1); +#else + argvec[count].partial = 0; +#endif + + locate_and_pad_parm (mode, NULL_TREE, + argvec[count].reg && argvec[count].partial == 0, + NULL_TREE, &args_size, &argvec[count].offset, + &argvec[count].size); + + if (argvec[count].size.var) + abort (); + +#ifndef REG_PARM_STACK_SPACE + if (argvec[count].partial) + argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD; +#endif + + if (argvec[count].reg == 0 || argvec[count].partial != 0 +#ifdef REG_PARM_STACK_SPACE + || 1 +#endif + ) + args_size.constant += argvec[count].size.constant; + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* If this arg is actually passed on the stack, it might be + clobbering something we already put there (this library call might + be inside the evaluation of an argument to a function whose call + requires the stack). This will only occur when the library call + has sufficient args to run out of argument registers. Abort in + this case; if this ever occurs, code must be added to save and + restore the arg slot. */ + + if (argvec[count].reg == 0 || argvec[count].partial != 0) + abort (); +#endif + + FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1); + } + va_end (p); + + /* If this machine requires an external definition for library + functions, write one out. */ + assemble_external_libcall (fun); + + original_args_size = args_size; +#ifdef STACK_BOUNDARY + args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) + / STACK_BYTES) * STACK_BYTES); +#endif + +#ifdef REG_PARM_STACK_SPACE + args_size.constant = MAX (args_size.constant, + REG_PARM_STACK_SPACE (NULL_TREE)); +#ifndef OUTGOING_REG_PARM_STACK_SPACE + args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE); +#endif +#endif + +#ifdef ACCUMULATE_OUTGOING_ARGS + if (args_size.constant > current_function_outgoing_args_size) + current_function_outgoing_args_size = args_size.constant; + args_size.constant = 0; +#endif + +#ifndef PUSH_ROUNDING + argblock = push_block (GEN_INT (args_size.constant), 0, 0); +#endif + +#ifdef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we push args individually in reverse order, perform stack alignment + before the first push (the last arg). */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + +#ifdef PUSH_ARGS_REVERSED + inc = -1; + argnum = nargs - 1; +#else + inc = 1; + argnum = 0; +#endif + + /* Push the args that need to be pushed. */ + + for (count = 0; count < nargs; count++, argnum += inc) + { + register enum machine_mode mode = argvec[argnum].mode; + register rtx val = argvec[argnum].value; + rtx reg = argvec[argnum].reg; + int partial = argvec[argnum].partial; + + if (! (reg != 0 && partial == 0)) + emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0, + argblock, GEN_INT (argvec[count].offset.constant)); + NO_DEFER_POP; + } + +#ifndef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we pushed args in forward order, perform stack alignment + after pushing the last arg. */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + +#ifdef PUSH_ARGS_REVERSED + argnum = nargs - 1; +#else + argnum = 0; +#endif + + /* Now load any reg parms into their regs. */ + + for (count = 0; count < nargs; count++, argnum += inc) + { + register enum machine_mode mode = argvec[argnum].mode; + register rtx val = argvec[argnum].value; + rtx reg = argvec[argnum].reg; + int partial = argvec[argnum].partial; + + if (reg != 0 && partial == 0) + emit_move_insn (reg, val); + NO_DEFER_POP; + } + + /* For version 1.37, try deleting this entirely. */ + if (! no_queue) + emit_queue (); + + /* Any regs containing parms remain in use through the call. */ + start_sequence (); + for (count = 0; count < nargs; count++) + if (argvec[count].reg != 0) + emit_insn (gen_rtx (USE, VOIDmode, argvec[count].reg)); + + use_insns = get_insns (); + end_sequence (); + + fun = prepare_call_address (fun, NULL_TREE, &use_insns); + + /* Don't allow popping to be deferred, since then + cse'ing of library calls could delete a call and leave the pop. */ + NO_DEFER_POP; + + /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which + will set inhibit_defer_pop to that value. */ + + emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size.constant, 0, + FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), + outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX, + old_inhibit_defer_pop + 1, use_insns, no_queue); + + /* Now restore inhibit_defer_pop to its actual original value. */ + OK_DEFER_POP; +} + +/* Like emit_library_call except that an extra argument, VALUE, + comes second and says where to store the result. + (If VALUE is zero, the result comes in the function value register.) */ + +void +emit_library_call_value (va_alist) + va_dcl +{ + va_list p; + /* Total size in bytes of all the stack-parms scanned so far. */ + struct args_size args_size; + /* Size of arguments before any adjustments (such as rounding). */ + struct args_size original_args_size; + register int argnum; + enum machine_mode outmode; + int nargs; + rtx fun; + rtx orgfun; + int inc; + int count; + rtx argblock = 0; + CUMULATIVE_ARGS args_so_far; + struct arg { rtx value; enum machine_mode mode; rtx reg; int partial; + struct args_size offset; struct args_size size; }; + struct arg *argvec; + int old_inhibit_defer_pop = inhibit_defer_pop; + int no_queue = 0; + rtx use_insns; + rtx value; + rtx mem_value = 0; + + va_start (p); + orgfun = fun = va_arg (p, rtx); + value = va_arg (p, rtx); + no_queue = va_arg (p, int); + outmode = va_arg (p, enum machine_mode); + nargs = va_arg (p, int); + + /* If this kind of value comes back in memory, + decide where in memory it should come back. */ + if (RETURN_IN_MEMORY (type_for_mode (outmode, 0))) + { + if (GET_CODE (value) == MEM) + mem_value = value; + else + mem_value = assign_stack_temp (outmode, GET_MODE_SIZE (outmode), 0); + } + + /* ??? Unfinished: must pass the memory address as an argument. */ + + /* Copy all the libcall-arguments out of the varargs data + and into a vector ARGVEC. + + Compute how to pass each argument. We only support a very small subset + of the full argument passing conventions to limit complexity here since + library functions shouldn't have many args. */ + + argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg)); + + INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun); + + args_size.constant = 0; + args_size.var = 0; + + count = 0; + + /* If there's a structure value address to be passed, + either pass it in the special place, or pass it as an extra argument. */ + if (mem_value) + { + rtx addr = XEXP (mem_value, 0); + + if (! struct_value_rtx) + { + nargs++; + + /* Make sure it is a reasonable operand for a move or push insn. */ + if (GET_CODE (addr) != REG && GET_CODE (addr) != MEM + && ! (CONSTANT_P (addr) && LEGITIMATE_CONSTANT_P (addr))) + addr = force_operand (addr, NULL_RTX); + + argvec[count].value = addr; + argvec[count].mode = outmode; + argvec[count].partial = 0; + + argvec[count].reg = FUNCTION_ARG (args_so_far, outmode, NULL_TREE, 1); +#ifdef FUNCTION_ARG_PARTIAL_NREGS + if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, outmode, NULL_TREE, 1)) + abort (); +#endif + + locate_and_pad_parm (outmode, NULL_TREE, + argvec[count].reg && argvec[count].partial == 0, + NULL_TREE, &args_size, &argvec[count].offset, + &argvec[count].size); + + + if (argvec[count].reg == 0 || argvec[count].partial != 0 +#ifdef REG_PARM_STACK_SPACE + || 1 +#endif + ) + args_size.constant += argvec[count].size.constant; + + FUNCTION_ARG_ADVANCE (args_so_far, outmode, (tree)0, 1); + } + } + + for (; count < nargs; count++) + { + rtx val = va_arg (p, rtx); + enum machine_mode mode = va_arg (p, enum machine_mode); + + /* We cannot convert the arg value to the mode the library wants here; + must do it earlier where we know the signedness of the arg. */ + if (mode == BLKmode + || (GET_MODE (val) != mode && GET_MODE (val) != VOIDmode)) + abort (); + + /* On some machines, there's no way to pass a float to a library fcn. + Pass it as a double instead. */ +#ifdef LIBGCC_NEEDS_DOUBLE + if (LIBGCC_NEEDS_DOUBLE && mode == SFmode) + val = convert_to_mode (DFmode, val, 0), mode = DFmode; +#endif + + /* There's no need to call protect_from_queue, because + either emit_move_insn or emit_push_insn will do that. */ + + /* Make sure it is a reasonable operand for a move or push insn. */ + if (GET_CODE (val) != REG && GET_CODE (val) != MEM + && ! (CONSTANT_P (val) && LEGITIMATE_CONSTANT_P (val))) + val = force_operand (val, NULL_RTX); + + argvec[count].value = val; + argvec[count].mode = mode; + +#ifdef FUNCTION_ARG_PASS_BY_REFERENCE + if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, mode, NULL_TREE, 1)) + abort (); +#endif + + argvec[count].reg = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1); + if (argvec[count].reg && GET_CODE (argvec[count].reg) == EXPR_LIST) + abort (); +#ifdef FUNCTION_ARG_PARTIAL_NREGS + argvec[count].partial + = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, NULL_TREE, 1); +#else + argvec[count].partial = 0; +#endif + + locate_and_pad_parm (mode, NULL_TREE, + argvec[count].reg && argvec[count].partial == 0, + NULL_TREE, &args_size, &argvec[count].offset, + &argvec[count].size); + + if (argvec[count].size.var) + abort (); + +#ifndef REG_PARM_STACK_SPACE + if (argvec[count].partial) + argvec[count].size.constant -= argvec[count].partial * UNITS_PER_WORD; +#endif + + if (argvec[count].reg == 0 || argvec[count].partial != 0 +#ifdef REG_PARM_STACK_SPACE + || 1 +#endif + ) + args_size.constant += argvec[count].size.constant; + +#ifdef ACCUMULATE_OUTGOING_ARGS + /* If this arg is actually passed on the stack, it might be + clobbering something we already put there (this library call might + be inside the evaluation of an argument to a function whose call + requires the stack). This will only occur when the library call + has sufficient args to run out of argument registers. Abort in + this case; if this ever occurs, code must be added to save and + restore the arg slot. */ + + if (argvec[count].reg == 0 || argvec[count].partial != 0) + abort (); +#endif + + FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree)0, 1); + } + va_end (p); + + /* If this machine requires an external definition for library + functions, write one out. */ + assemble_external_libcall (fun); + + original_args_size = args_size; +#ifdef STACK_BOUNDARY + args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) + / STACK_BYTES) * STACK_BYTES); +#endif + +#ifdef REG_PARM_STACK_SPACE + args_size.constant = MAX (args_size.constant, + REG_PARM_STACK_SPACE (NULL_TREE)); +#ifndef OUTGOING_REG_PARM_STACK_SPACE + args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE); +#endif +#endif + +#ifdef ACCUMULATE_OUTGOING_ARGS + if (args_size.constant > current_function_outgoing_args_size) + current_function_outgoing_args_size = args_size.constant; + args_size.constant = 0; +#endif + +#ifndef PUSH_ROUNDING + argblock = push_block (GEN_INT (args_size.constant), 0, 0); +#endif + +#ifdef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we push args individually in reverse order, perform stack alignment + before the first push (the last arg). */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + +#ifdef PUSH_ARGS_REVERSED + inc = -1; + argnum = nargs - 1; +#else + inc = 1; + argnum = 0; +#endif + + /* Push the args that need to be pushed. */ + + for (count = 0; count < nargs; count++, argnum += inc) + { + register enum machine_mode mode = argvec[argnum].mode; + register rtx val = argvec[argnum].value; + rtx reg = argvec[argnum].reg; + int partial = argvec[argnum].partial; + + if (! (reg != 0 && partial == 0)) + emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0, + argblock, GEN_INT (argvec[count].offset.constant)); + NO_DEFER_POP; + } + +#ifndef PUSH_ARGS_REVERSED +#ifdef STACK_BOUNDARY + /* If we pushed args in forward order, perform stack alignment + after pushing the last arg. */ + if (argblock == 0) + anti_adjust_stack (GEN_INT (args_size.constant + - original_args_size.constant)); +#endif +#endif + +#ifdef PUSH_ARGS_REVERSED + argnum = nargs - 1; +#else + argnum = 0; +#endif + + /* Now load any reg parms into their regs. */ + + if (mem_value != 0 && struct_value_rtx != 0) + emit_move_insn (struct_value_rtx, XEXP (mem_value, 0)); + + for (count = 0; count < nargs; count++, argnum += inc) + { + register enum machine_mode mode = argvec[argnum].mode; + register rtx val = argvec[argnum].value; + rtx reg = argvec[argnum].reg; + int partial = argvec[argnum].partial; + + if (reg != 0 && partial == 0) + emit_move_insn (reg, val); + NO_DEFER_POP; + } + +#if 0 + /* For version 1.37, try deleting this entirely. */ + if (! no_queue) + emit_queue (); +#endif + + /* Any regs containing parms remain in use through the call. */ + start_sequence (); + for (count = 0; count < nargs; count++) + if (argvec[count].reg != 0) + emit_insn (gen_rtx (USE, VOIDmode, argvec[count].reg)); + + use_insns = get_insns (); + end_sequence (); + + fun = prepare_call_address (fun, NULL_TREE, &use_insns); + + /* Don't allow popping to be deferred, since then + cse'ing of library calls could delete a call and leave the pop. */ + NO_DEFER_POP; + + /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which + will set inhibit_defer_pop to that value. */ + + emit_call_1 (fun, get_identifier (XSTR (orgfun, 0)), args_size.constant, 0, + FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), + outmode != VOIDmode ? hard_libcall_value (outmode) : NULL_RTX, + old_inhibit_defer_pop + 1, use_insns, no_queue); + + /* Now restore inhibit_defer_pop to its actual original value. */ + OK_DEFER_POP; + + /* Copy the value to the right place. */ + if (outmode != VOIDmode) + { + if (mem_value) + { + if (value == 0) + value = hard_libcall_value (outmode); + if (value != mem_value) + emit_move_insn (value, mem_value); + } + else if (value != 0) + emit_move_insn (value, hard_libcall_value (outmode)); + } +} + #if 0 /* Return an rtx which represents a suitable home on the stack given TYPE, the type of the argument looking for a home.