aboutsummaryrefslogtreecommitdiff
path: root/gcc/builtins.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r--gcc/builtins.c330
1 files changed, 274 insertions, 56 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 0a833fc..6d622ff 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -73,9 +73,11 @@ static void expand_builtin_return PROTO((rtx));
static rtx expand_builtin_classify_type PROTO((tree));
static rtx expand_builtin_mathfn PROTO((tree, rtx, rtx));
static rtx expand_builtin_constant_p PROTO((tree));
-static rtx expand_builtin_saveregs PROTO((tree, rtx, int));
static rtx expand_builtin_args_info PROTO((tree));
static rtx expand_builtin_next_arg PROTO((tree));
+static rtx expand_builtin_va_start PROTO((int, tree));
+static rtx expand_builtin_va_end PROTO((tree));
+static rtx expand_builtin_va_copy PROTO((tree));
static rtx expand_builtin_memcmp PROTO((tree, tree, rtx));
static rtx expand_builtin_strcmp PROTO((tree, rtx));
static rtx expand_builtin_memcpy PROTO((tree));
@@ -1672,68 +1674,53 @@ expand_builtin_strcmp (exp, target)
}
#endif
-/* Expand expression EXP, which is a call to __builtin_saveregs,
- generating the result in TARGET, if that's convenient.
- IGNORE is nonzero if the value is to be ignored. */
-static rtx
-expand_builtin_saveregs (exp, target, ignore)
- tree exp;
- rtx target;
- int ignore;
+/* Expand a call to __builtin_saveregs, generating the result in TARGET,
+ if that's convenient. */
+rtx
+expand_builtin_saveregs ()
{
- enum machine_mode value_mode = TYPE_MODE (TREE_TYPE (exp));
+ rtx val, seq;
/* Don't do __builtin_saveregs more than once in a function.
Save the result of the first call and reuse it. */
if (saveregs_value != 0)
return saveregs_value;
- {
- /* When this function is called, it means that registers must be
- saved on entry to this function. So we migrate the
- call to the first insn of this function. */
- rtx temp;
- rtx seq;
- /* Now really call the function. `expand_call' does not call
- expand_builtin, so there is no danger of infinite recursion here. */
- start_sequence ();
+ /* When this function is called, it means that registers must be
+ saved on entry to this function. So we migrate the call to the
+ first insn of this function. */
+
+ start_sequence ();
#ifdef EXPAND_BUILTIN_SAVEREGS
- /* Do whatever the machine needs done in this case. */
- temp = EXPAND_BUILTIN_SAVEREGS (arglist);
+ /* Do whatever the machine needs done in this case. */
+ val = EXPAND_BUILTIN_SAVEREGS ();
#else
- /* The register where the function returns its value
- is likely to have something else in it, such as an argument.
- So preserve that register around the call. */
-
- if (value_mode != VOIDmode)
- {
- rtx valreg = hard_libcall_value (value_mode);
- rtx saved_valreg = gen_reg_rtx (value_mode);
-
- emit_move_insn (saved_valreg, valreg);
- temp = expand_call (exp, target, ignore);
- emit_move_insn (valreg, saved_valreg);
- }
- else
- /* Generate the call, putting the value in a pseudo. */
- temp = expand_call (exp, target, ignore);
+ /* ??? We used to try and build up a call to the out of line function,
+ guessing about what registers needed saving etc. This became much
+ harder with __builtin_va_start, since we don't have a tree for a
+ call to __builtin_saveregs to fall back on. There was exactly one
+ port (i860) that used this code, and I'm unconvinced it could actually
+ handle the general case. So we no longer try to handle anything
+ weird and make the backend absorb the evil. */
+
+ error ("__builtin_saveregs not supported by this target");
+ val = const0_rtx;
#endif
- seq = get_insns ();
- end_sequence ();
+ seq = get_insns ();
+ end_sequence ();
- saveregs_value = temp;
+ saveregs_value = val;
- /* Put the sequence after the NOTE that starts the function.
- If this is inside a SEQUENCE, make the outer-level insn
- chain current, so the code is placed at the start of the
- function. */
- push_topmost_sequence ();
- emit_insns_before (seq, NEXT_INSN (get_insns ()));
- pop_topmost_sequence ();
- return temp;
- }
+ /* Put the sequence after the NOTE that starts the function. If this
+ is inside a SEQUENCE, make the outer-level insn chain current, so
+ the code is placed at the start of the function. */
+ push_topmost_sequence ();
+ emit_insns_after (seq, get_insns ());
+ pop_topmost_sequence ();
+
+ return val;
}
/* __builtin_args_info (N) returns word N of the arg space info
@@ -1785,18 +1772,17 @@ expand_builtin_args_info (exp)
result = build (CONSTRUCTOR, type, NULL_TREE, nreverse (elts));
TREE_CONSTANT (result) = 1;
TREE_STATIC (result) = 1;
- result = build (INDIRECT_REF, build_pointer_type (type), result);
+ result = build1 (INDIRECT_REF, build_pointer_type (type), result);
TREE_CONSTANT (result) = 1;
return expand_expr (result, NULL_RTX, VOIDmode, EXPAND_MEMORY_USE_BAD);
#endif
}
-/* Expand expression EXP, which is a call to __builtin_next_arg. */
+/* Expand ARGLIST, from a call to __builtin_next_arg. */
static rtx
-expand_builtin_next_arg (exp)
- tree exp;
+expand_builtin_next_arg (arglist)
+ tree arglist;
{
- tree arglist = TREE_OPERAND (exp, 1);
tree fntype = TREE_TYPE (current_function_decl);
if ((TYPE_ARG_TYPES (fntype) == 0
@@ -1836,6 +1822,230 @@ expand_builtin_next_arg (exp)
NULL_RTX, 0, OPTAB_LIB_WIDEN);
}
+/* Make it easier for the backends by protecting the valist argument
+ from multiple evaluations. */
+
+static tree
+stabilize_va_list (valist, was_ptr)
+ tree valist;
+ int was_ptr;
+{
+ int is_array = TREE_CODE (va_list_type_node) == ARRAY_TYPE;
+
+ if (was_ptr)
+ {
+ /* If stdarg.h took the address of an array-type valist that was passed
+ as a parameter, we'll have taken the address of the parameter itself
+ rather than the array as we'd intended. Undo this mistake. */
+ if (is_array
+ && TREE_CODE (valist) == ADDR_EXPR
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (valist, 0))) == POINTER_TYPE)
+ valist = TREE_OPERAND (valist, 0);
+
+ if (TREE_SIDE_EFFECTS (valist))
+ valist = save_expr (valist);
+
+ if (! is_array)
+ valist = fold (build1 (INDIRECT_REF, va_list_type_node, valist));
+ }
+ else if (TREE_SIDE_EFFECTS (valist))
+ {
+ if (is_array)
+ valist = save_expr (valist);
+ else
+ {
+ valist = build1 (ADDR_EXPR, build_pointer_type (va_list_type_node),
+ valist);
+ TREE_SIDE_EFFECTS (valist) = 1;
+ valist = save_expr (valist);
+ valist = fold (build1 (INDIRECT_REF, va_list_type_node, valist));
+ }
+ }
+
+ return valist;
+}
+
+/* The "standard" implementation of va_start: just assign `nextarg' to
+ the variable. */
+void
+std_expand_builtin_va_start (stdarg_p, valist, nextarg)
+ int stdarg_p ATTRIBUTE_UNUSED;
+ tree valist;
+ rtx nextarg;
+{
+ tree t;
+
+ t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ make_tree (ptr_type_node, nextarg));
+ TREE_SIDE_EFFECTS (t) = 1;
+
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+}
+
+/* Expand ARGLIST, which from a call to __builtin_stdarg_va_start or
+ __builtin_varargs_va_start, depending on STDARG_P. */
+static rtx
+expand_builtin_va_start (stdarg_p, arglist)
+ int stdarg_p;
+ tree arglist;
+{
+ rtx nextarg;
+ tree chain = arglist, valist;
+
+ if (stdarg_p)
+ nextarg = expand_builtin_next_arg (chain = TREE_CHAIN (arglist));
+ else
+ nextarg = expand_builtin_next_arg (NULL_TREE);
+
+ if (TREE_CHAIN (chain))
+ error ("too many arguments to function `va_start'");
+
+ valist = stabilize_va_list (TREE_VALUE (arglist), 1);
+
+#ifdef EXPAND_BUILTIN_VA_START
+ EXPAND_BUILTIN_VA_START (stdarg_p, valist, nextarg);
+#else
+ std_expand_builtin_va_start (stdarg_p, valist, nextarg);
+#endif
+
+ return const0_rtx;
+}
+
+/* Allocate an alias set for use in storing and reading from the varargs
+ spill area. */
+int
+get_varargs_alias_set ()
+{
+ static int set = -1;
+ if (set == -1)
+ set = new_alias_set ();
+ return set;
+}
+
+/* The "standard" implementation of va_arg: read the value from the
+ current (padded) address and increment by the (padded) size. */
+rtx
+std_expand_builtin_va_arg (valist, type)
+ tree valist, type;
+{
+ tree addr_tree, t;
+ HOST_WIDE_INT align;
+ HOST_WIDE_INT rounded_size;
+ rtx addr;
+
+ /* Compute the rounded size of the type. */
+ align = PARM_BOUNDARY / BITS_PER_UNIT;
+ rounded_size = (((TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_UNIT
+ + align - 1) / align) * align);
+
+ /* Get AP. */
+ addr_tree = valist;
+ if (BYTES_BIG_ENDIAN)
+ {
+ /* Small args are padded downward. */
+
+ HOST_WIDE_INT adj;
+ adj = TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_UNIT;
+ if (rounded_size > align)
+ adj = rounded_size;
+
+ addr_tree = build (PLUS_EXPR, TREE_TYPE (addr_tree), addr_tree,
+ build_int_2 (rounded_size - adj, 0));
+ }
+
+ addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
+ addr = copy_to_reg (addr);
+
+ /* Compute new value for AP. */
+ t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ build (PLUS_EXPR, TREE_TYPE (valist), valist,
+ build_int_2 (rounded_size, 0)));
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ return addr;
+}
+
+/* Expand __builtin_va_arg, which is not really a builtin function, but
+ a very special sort of operator. */
+rtx
+expand_builtin_va_arg (valist, type)
+ tree valist, type;
+{
+ rtx addr, result;
+
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (valist))
+ != TYPE_MAIN_VARIANT (va_list_type_node))
+ {
+ error ("first argument to `__builtin_va_arg' not of type `va_list'");
+ addr = const0_rtx;
+ }
+ else
+ {
+ /* Make it easier for the backends by protecting the valist argument
+ from multiple evaluations. */
+ valist = stabilize_va_list (valist, 0);
+
+#ifdef EXPAND_BUILTIN_VA_ARG
+ addr = EXPAND_BUILTIN_VA_ARG (valist, type);
+#else
+ addr = std_expand_builtin_va_arg (valist, type);
+#endif
+ }
+
+ result = gen_rtx_MEM (TYPE_MODE (type), addr);
+ MEM_ALIAS_SET (result) = get_varargs_alias_set ();
+
+ return result;
+}
+
+/* Expand ARGLIST, from a call to __builtin_va_end. */
+static rtx
+expand_builtin_va_end (arglist)
+ tree arglist ATTRIBUTE_UNUSED;
+{
+#ifdef EXPAND_BUILTIN_VA_END
+ tree valist = TREE_VALUE (arglist, 0);
+ valist = stabilize_va_list (valist, 0);
+ EXPAND_BUILTIN_VA_END(arglist);
+#endif
+
+ return const0_rtx;
+}
+
+/* Expand ARGLIST, from a call to __builtin_va_copy. We do this as a
+ builtin rather than just as an assignment in stdarg.h because of the
+ nastiness of array-type va_list types. */
+static rtx
+expand_builtin_va_copy (arglist)
+ tree arglist;
+{
+ tree dst, src, t;
+
+ dst = TREE_VALUE (arglist);
+ src = TREE_VALUE (TREE_CHAIN (arglist));
+
+ dst = stabilize_va_list (dst, 1);
+ src = stabilize_va_list (src, 0);
+
+ if (TREE_CODE (va_list_type_node) != ARRAY_TYPE)
+ {
+ t = build (MODIFY_EXPR, va_list_type_node, dst, src);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+ else
+ {
+ emit_block_move (expand_expr (dst, NULL_RTX, Pmode, EXPAND_NORMAL),
+ expand_expr (src, NULL_RTX, Pmode, EXPAND_NORMAL),
+ expand_expr (TYPE_SIZE (va_list_type_node), NULL_RTX,
+ VOIDmode, EXPAND_NORMAL),
+ TYPE_ALIGN (va_list_type_node) / BITS_PER_UNIT);
+ }
+
+ return const0_rtx;
+}
+
/* Expand a call to one of the builtin functions __builtin_frame_address or
__builtin_return_address. */
static rtx
@@ -2031,14 +2241,14 @@ expand_builtin (exp, target, subtarget, mode, ignore)
return const0_rtx;
case BUILT_IN_SAVEREGS:
- return expand_builtin_saveregs (exp, target, ignore);
+ return expand_builtin_saveregs ();
case BUILT_IN_ARGS_INFO:
return expand_builtin_args_info (exp);
/* Return the address of the first anonymous stack arg. */
case BUILT_IN_NEXT_ARG:
- return expand_builtin_next_arg (exp);
+ return expand_builtin_next_arg (arglist);
case BUILT_IN_CLASSIFY_TYPE:
return expand_builtin_classify_type (arglist);
@@ -2186,6 +2396,14 @@ expand_builtin (exp, target, subtarget, mode, ignore)
TREE_VALUE (TREE_CHAIN (arglist)),
TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))));
return const0_rtx;
+ case BUILT_IN_VARARGS_START:
+ return expand_builtin_va_start (0, arglist);
+ case BUILT_IN_STDARG_START:
+ return expand_builtin_va_start (1, arglist);
+ case BUILT_IN_VA_END:
+ return expand_builtin_va_end (arglist);
+ case BUILT_IN_VA_COPY:
+ return expand_builtin_va_copy (arglist);
default: /* just do library call, if unknown builtin */
error ("built-in function `%s' not currently supported",