summaryrefslogtreecommitdiff
path: root/gcc/expr.c
diff options
context:
space:
mode:
authorrms <rms@138bc75d-0d04-0410-961f-82ee72b054a4>1993-02-02 04:26:15 +0000
committerrms <rms@138bc75d-0d04-0410-961f-82ee72b054a4>1993-02-02 04:26:15 +0000
commit5b11e107672a5106217fd2aab6558f3557a8ce05 (patch)
treed1cfaa8cacef24a47b2b71baaba10bd631ddc656 /gcc/expr.c
parent1bb8cc8097913da64303e77f6be825eb843c552a (diff)
downloadgcc-5b11e107672a5106217fd2aab6558f3557a8ce05.tar.gz
(expand_builtin): Report overflow if __builtin_args_info
arg exceeds one word. Fix punctuation of error messages. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@3400 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/expr.c')
-rw-r--r--gcc/expr.c265
1 files changed, 258 insertions, 7 deletions
diff --git a/gcc/expr.c b/gcc/expr.c
index 6b4a57a3526..b1ce3ca66fb 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -2293,6 +2293,257 @@ emit_library_call (va_alist)
OK_DEFER_POP;
}
+/* Like emit_library_call except that an extra argument, VALUE,
+ comes second and says where to store the result. */
+
+void
+emit_library_call_value (va_alist)
+ va_dcl
+{
+ va_list p;
+ struct args_size 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 * 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);
+
+#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
+ 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;
+ }
+
+#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;
+
+ /* Copy the value to the right place. */
+ if (mem_value)
+ {
+ if (value != mem_value)
+ emit_move_insn (value, mem_value);
+ }
+ else
+ emit_move_insn (value, hard_libcall_value (outmode));
+}
+
/* Expand an assignment that stores the value of FROM into TO.
If WANT_VALUE is nonzero, return an rtx for the value of TO.
(This may contain a QUEUED rtx.)
@@ -5795,19 +6046,19 @@ expand_builtin (exp, target, subtarget, mode, ignore)
{
tree arg = TREE_VALUE (arglist);
if (TREE_CODE (arg) != INTEGER_CST)
- error ("argument of __builtin_args_info must be constant");
+ error ("argument of `__builtin_args_info' must be constant");
else
{
int wordnum = TREE_INT_CST_LOW (arg);
- if (wordnum < 0 || wordnum >= nwords)
- error ("argument of __builtin_args_info out of range");
+ if (wordnum < 0 || wordnum >= nwords || TREE_INT_CST_HIGH (arg))
+ error ("argument of `__builtin_args_info' out of range");
else
return GEN_INT (word_ptr[wordnum]);
}
}
else
- error ("missing argument in __builtin_args_info");
+ error ("missing argument in `__builtin_args_info'");
return const0_rtx;
@@ -5910,12 +6161,12 @@ expand_builtin (exp, target, subtarget, mode, ignore)
return const0_rtx;
else if (TREE_CODE (TREE_VALUE (arglist)) != INTEGER_CST)
{
- error ("invalid arg to __builtin_return_address");
+ error ("invalid arg to `__builtin_return_address'");
return const0_rtx;
}
else if (tree_int_cst_lt (TREE_VALUE (arglist), integer_zero_node))
{
- error ("invalid arg to __builtin_return_address");
+ error ("invalid arg to `__builtin_return_address'");
return const0_rtx;
}
else
@@ -6272,7 +6523,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
#endif
default: /* just do library call, if unknown builtin */
- error ("built-in function %s not currently supported",
+ error ("built-in function `%s' not currently supported",
IDENTIFIER_POINTER (DECL_NAME (fndecl)));
}