+2004-03-23 Zdenek Dvorak <rakdver@atrey.karlin.mff.cuni.cz>
+
+ * Makefile.in (LIBGCOV): Add _gcov_fork, _gcov_execl, _gcov_execlp,
+ _gcov_execle, _gcov_execv, _gcov_execvp, _gcov_execve.
+ * builtin-types.def (BT_PID, BT_PTR_CONST_STRING, BT_FN_PID,
+ BT_FN_INT_CONST_STRING_PTR_CONST_STRING,
+ BT_FN_INT_CONST_STRING_PTR_CONST_STRING_PTR_CONST_STRING): New.
+ * builtins.c (expand_builtin_fork_or_exec): New.
+ (expand_builtin): Call it.
+ * builtins.def (BUILT_IN_EXECL, BUILT_IN_EXECLP,BUILT_IN_EXECLE,
+ BUILT_IN_EXECV, BUILT_IN_EXECVP, BUILT_IN_EXECVE, BUILT_IN_FORK): New.
+ * c-common.c (PID_TYPE): New macro.
+ (c_common_nodes_and_builtins): Initialize pid_type_node.
+ * calls.c (special_function_p): Do not handle fork and exec.
+ (expand_call): Do not handle ECF_FORK_OR_EXEC.
+ * gcov-io.h (__gcov_fork, __gcov_execl, __gcov_execlp, __gcov_execle,
+ __gcov_execv, __gcov_execvp, __gcov_execve): Declare.
+ * libgcov.c (__gcov_fork, __gcov_execl, __gcov_execlp, __gcov_execle,
+ __gcov_execv, __gcov_execvp, __gcov_execve): New.
+ * tree.h (enum tree_index): Add TI_PID_TYPE.
+ (pid_type_node): New macro.
+ (ECF_FORK_OR_EXEC): Removed.
+
2004-04-23 Eric Botcazou <ebotcazou@libertysurf.fr>
PR optimization/13985
LIB2FUNCS_ST = _eprintf __gcc_bcmp
# Defined in libgcov.c, included only in gcov library
-LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta
+LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
+ _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
+ _gcov_execv _gcov_execvp _gcov_execve
FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \
_fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \
DEF_PRIMITIVE_TYPE (BT_FLOAT_PTR, float_ptr_type_node)
DEF_PRIMITIVE_TYPE (BT_DOUBLE_PTR, double_ptr_type_node)
DEF_PRIMITIVE_TYPE (BT_LONGDOUBLE_PTR, long_double_ptr_type_node)
+DEF_PRIMITIVE_TYPE (BT_PID, pid_type_node)
DEF_PRIMITIVE_TYPE (BT_SIZE, size_type_node)
DEF_PRIMITIVE_TYPE (BT_SSIZE, signed_size_type_node)
DEF_PRIMITIVE_TYPE (BT_WINT, wint_type_node)
DEF_PRIMITIVE_TYPE (BT_VALIST_REF, va_list_ref_type_node)
DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
+DEF_POINTER_TYPE (BT_PTR_CONST_STRING, BT_CONST_STRING)
+
DEF_FUNCTION_TYPE_0 (BT_FN_VOID, BT_VOID)
DEF_FUNCTION_TYPE_0 (BT_FN_PTR, BT_PTR)
+DEF_FUNCTION_TYPE_0 (BT_FN_PID, BT_PID)
DEF_FUNCTION_TYPE_0 (BT_FN_UNSIGNED, BT_UNSIGNED)
DEF_FUNCTION_TYPE_0 (BT_FN_FLOAT, BT_FLOAT)
DEF_FUNCTION_TYPE_0 (BT_FN_DOUBLE, BT_DOUBLE)
BT_COMPLEX_DOUBLE, BT_COMPLEX_DOUBLE, BT_COMPLEX_DOUBLE)
DEF_FUNCTION_TYPE_2 (BT_FN_COMPLEX_LONGDOUBLE_COMPLEX_LONGDOUBLE_COMPLEX_LONGDOUBLE,
BT_COMPLEX_LONGDOUBLE, BT_COMPLEX_LONGDOUBLE, BT_COMPLEX_LONGDOUBLE)
+DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING,
+ BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING)
DEF_FUNCTION_TYPE_3 (BT_FN_STRING_STRING_CONST_STRING_SIZE,
BT_STRING, BT_STRING, BT_CONST_STRING, BT_SIZE)
BT_VOID, BT_DOUBLE, BT_DOUBLE_PTR, BT_DOUBLE_PTR)
DEF_FUNCTION_TYPE_3 (BT_FN_VOID_LONGDOUBLE_LONGDOUBLEPTR_LONGDOUBLEPTR,
BT_VOID, BT_LONGDOUBLE, BT_LONGDOUBLE_PTR, BT_LONGDOUBLE_PTR)
+DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_STRING_PTR_CONST_STRING_PTR_CONST_STRING,
+ BT_INT, BT_CONST_STRING, BT_PTR_CONST_STRING, BT_PTR_CONST_STRING)
DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_PTR,
BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_PTR)
}
return temp;
}
+
+/* Expand fork or exec calls. TARGET is the desired target of the
+ call. ARGLIST is the list of arguments of the call. FN is the
+ identificator of the actual function. IGNORE is nonzero if the
+ value is to be ignored. */
+
+static rtx
+expand_builtin_fork_or_exec (tree fn, tree arglist, rtx target, int ignore)
+{
+ tree id, decl;
+ tree call;
+
+ /* If we are not profiling, just call the function. */
+ if (!profile_arc_flag)
+ return NULL_RTX;
+
+ /* Otherwise call the wrapper. This should be equivalent for the rest of
+ compiler, so the code does not diverge, and the wrapper may run the
+ code neccesary for keeping the profiling sane. */
+
+ switch (DECL_FUNCTION_CODE (fn))
+ {
+ case BUILT_IN_FORK:
+ id = get_identifier ("__gcov_fork");
+ break;
+
+ case BUILT_IN_EXECL:
+ id = get_identifier ("__gcov_execl");
+ break;
+
+ case BUILT_IN_EXECV:
+ id = get_identifier ("__gcov_execv");
+ break;
+
+ case BUILT_IN_EXECLP:
+ id = get_identifier ("__gcov_execlp");
+ break;
+
+ case BUILT_IN_EXECLE:
+ id = get_identifier ("__gcov_execle");
+ break;
+
+ case BUILT_IN_EXECVP:
+ id = get_identifier ("__gcov_execvp");
+ break;
+
+ case BUILT_IN_EXECVE:
+ id = get_identifier ("__gcov_execve");
+ break;
+
+ default:
+ abort ();
+ }
+
+ decl = build_decl (FUNCTION_DECL, id, TREE_TYPE (fn));
+ DECL_EXTERNAL (decl) = 1;
+ TREE_PUBLIC (decl) = 1;
+ DECL_ARTIFICIAL (decl) = 1;
+ TREE_NOTHROW (decl) = 1;
+ call = build_function_call_expr (decl, arglist);
+
+ return expand_call (call, target, ignore);
+}
\f
/* Expand an expression EXP that calls a built-in function,
with result going to TARGET if that's convenient
expand_builtin_prefetch (arglist);
return const0_rtx;
+ case BUILT_IN_FORK:
+ case BUILT_IN_EXECL:
+ case BUILT_IN_EXECV:
+ case BUILT_IN_EXECLP:
+ case BUILT_IN_EXECLE:
+ case BUILT_IN_EXECVP:
+ case BUILT_IN_EXECVE:
+ target = expand_builtin_fork_or_exec (fndecl, arglist, target, ignore);
+ if (target)
+ return target;
+ break;
default: /* just do library call, if unknown builtin */
if (!DECL_ASSEMBLER_NAME_SET_P (fndecl))
DEF_GCC_BUILTIN (BUILT_IN_DWARF_SP_COLUMN, "dwarf_sp_column", BT_FN_UNSIGNED, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_EH_RETURN, "eh_return", BT_FN_VOID_PTRMODE_PTR, ATTR_NORETURN_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_EH_RETURN_DATA_REGNO, "eh_return_data_regno", BT_FN_INT_INT, ATTR_NULL)
+DEF_LIB_BUILTIN (BUILT_IN_EXECL, "execl", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_NOTHROW_LIST)
+DEF_LIB_BUILTIN (BUILT_IN_EXECLP, "execlp", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_NOTHROW_LIST)
+DEF_LIB_BUILTIN (BUILT_IN_EXECLE, "execle", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_NOTHROW_LIST)
+DEF_LIB_BUILTIN (BUILT_IN_EXECV, "execv", BT_FN_INT_CONST_STRING_PTR_CONST_STRING, ATTR_NOTHROW_LIST)
+DEF_LIB_BUILTIN (BUILT_IN_EXECVP, "execvp", BT_FN_INT_CONST_STRING_PTR_CONST_STRING, ATTR_NOTHROW_LIST)
+DEF_LIB_BUILTIN (BUILT_IN_EXECVE, "execve", BT_FN_INT_CONST_STRING_PTR_CONST_STRING_PTR_CONST_STRING, ATTR_NOTHROW_LIST)
DEF_LIB_BUILTIN (BUILT_IN_EXIT, "exit", BT_FN_VOID_INT, ATTR_NORETURN_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_EXPECT, "expect", BT_FN_LONG_LONG_LONG, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_EXTEND_POINTER, "extend_pointer", BT_FN_WORD_PTR, ATTR_CONST_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_EXTRACT_RETURN_ADDR, "extract_return_addr", BT_FN_PTR_PTR, ATTR_NULL)
+DEF_LIB_BUILTIN (BUILT_IN_FORK, "fork", BT_FN_PID, ATTR_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_FRAME_ADDRESS, "frame_address", BT_FN_PTR_UNSIGNED, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_FROB_RETURN_ADDR, "frob_return_addr", BT_FN_PTR_PTR, ATTR_NULL)
DEF_EXT_LIB_BUILTIN (BUILT_IN_GETTEXT, "gettext", BT_FN_STRING_CONST_STRING, ATTR_FORMAT_ARG_1)
#define SIZE_TYPE "long unsigned int"
#endif
+#ifndef PID_TYPE
+#define PID_TYPE "int"
+#endif
+
#ifndef WCHAR_TYPE
#define WCHAR_TYPE "int"
#endif
signed_size_type_node = c_common_signed_type (size_type_node);
set_sizetype (size_type_node);
+ pid_type_node =
+ TREE_TYPE (identifier_global_value (get_identifier (PID_TYPE)));
+
build_common_tree_nodes_2 (flag_short_double);
record_builtin_type (RID_FLOAT, NULL, float_type_node);
else if (tname[0] == 'l' && tname[1] == 'o'
&& ! strcmp (tname, "longjmp"))
flags |= ECF_LONGJMP;
-
- else if ((tname[0] == 'f' && tname[1] == 'o'
- && ! strcmp (tname, "fork"))
- /* Linux specific: __clone. check NAME to insist on the
- leading underscores, to avoid polluting the ISO / POSIX
- namespace. */
- || (name[0] == '_' && name[1] == '_'
- && ! strcmp (tname, "clone"))
- || (tname[0] == 'e' && tname[1] == 'x' && tname[2] == 'e'
- && tname[3] == 'c' && (tname[4] == 'l' || tname[4] == 'v')
- && (tname[5] == '\0'
- || ((tname[5] == 'p' || tname[5] == 'e')
- && tname[6] == '\0'))))
- flags |= ECF_FORK_OR_EXEC;
}
+
return flags;
}
stack_pointer_delta = save_stack_pointer_delta;
}
- if (profile_arc_flag && (flags & ECF_FORK_OR_EXEC))
- {
- /* A fork duplicates the profile information, and an exec discards
- it. We can't rely on fork/exec to be paired. So write out the
- profile information we have gathered so far, and clear it. */
- /* ??? When Linux's __clone is called with CLONE_VM set, profiling
- is subject to race conditions, just as with multithreaded
- programs. */
-
- emit_library_call (gcov_flush_libfunc, LCT_ALWAYS_RETURN, VOIDmode, 0);
- }
-
/* Ensure current function's preferred stack boundary is at least
what we need. We don't have to increase alignment for recursive
functions. */
/* The merge function to choose the most common difference between
consecutive values. */
extern void __gcov_merge_delta (gcov_type *, unsigned);
+
+/* The wrappers around some library functions.. */
+extern pid_t __gcov_fork (void);
+extern int __gcov_execl (const char *, const char *, ...);
+extern int __gcov_execlp (const char *, const char *, ...);
+extern int __gcov_execle (const char *, const char *, ...);
+extern int __gcov_execv (const char *, char *const []);
+extern int __gcov_execvp (const char *, char *const []);
+extern int __gcov_execve (const char *, char *const [], char *const []);
+
#endif /* IN_LIBGCOV */
#if IN_LIBGCOV >= 0
}
#endif /* L_gcov_merge_delta */
+#ifdef L_gcov_fork
+/* A wrapper for the fork function. Flushes the accumulated profiling data, so
+ that they are not counted twice. */
+
+pid_t
+__gcov_fork (void)
+{
+ __gcov_flush ();
+ return fork ();
+}
+#endif
+
+#ifdef L_gcov_execl
+/* A wrapper for the execl function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execl (const char *path, const char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = alloca (length * sizeof (void *));
+ args[0] = (char *) arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ va_end (aq);
+
+ return execv (path, args);
+}
+#endif
+
+#ifdef L_gcov_execlp
+/* A wrapper for the execlp function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execlp (const char *path, const char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = alloca (length * sizeof (void *));
+ args[0] = (char *) arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ va_end (aq);
+
+ return execvp (path, args);
+}
+#endif
+
+#ifdef L_gcov_execle
+/* A wrapper for the execle function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execle (const char *path, const char *arg, ...)
+{
+ va_list ap, aq;
+ unsigned i, length;
+ char **args;
+ char **envp;
+
+ __gcov_flush ();
+
+ va_start (ap, arg);
+ va_copy (aq, ap);
+
+ length = 2;
+ while (va_arg (ap, char *))
+ length++;
+ va_end (ap);
+
+ args = alloca (length * sizeof (void *));
+ args[0] = (char *) arg;
+ for (i = 1; i < length; i++)
+ args[i] = va_arg (aq, char *);
+ envp = va_arg (aq, char **);
+ va_end (aq);
+
+ return execve (path, args, envp);
+}
+#endif
+
+#ifdef L_gcov_execv
+/* A wrapper for the execv function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execv (const char *path, char *const argv[])
+{
+ __gcov_flush ();
+ return execv (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execvp
+/* A wrapper for the execvp function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execvp (const char *path, char *const argv[])
+{
+ __gcov_flush ();
+ return execvp (path, argv);
+}
+#endif
+
+#ifdef L_gcov_execve
+/* A wrapper for the execve function. Flushes the accumulated profiling data, so
+ that they are not lost. */
+
+int
+__gcov_execve (const char *path, char *const argv[], char *const envp[])
+{
+ __gcov_flush ();
+ return execve (path, argv, envp);
+}
+#endif
#endif /* inhibit_libc */
TI_PTR_TYPE,
TI_CONST_PTR_TYPE,
TI_SIZE_TYPE,
+ TI_PID_TYPE,
TI_PTRDIFF_TYPE,
TI_VA_LIST_TYPE,
TI_BOOLEAN_TYPE,
#define const_ptr_type_node global_trees[TI_CONST_PTR_TYPE]
/* The C type `size_t'. */
#define size_type_node global_trees[TI_SIZE_TYPE]
+#define pid_type_node global_trees[TI_PID_TYPE]
#define ptrdiff_type_node global_trees[TI_PTRDIFF_TYPE]
#define va_list_type_node global_trees[TI_VA_LIST_TYPE]
#define ECF_LONGJMP 64
/* Nonzero if this is a syscall that makes a new process in the image of
the current one. */
-#define ECF_FORK_OR_EXEC 128
-#define ECF_SIBCALL 256
+#define ECF_SIBCALL 128
/* Nonzero if this is a call to "pure" function (like const function,
but may read memory. */
-#define ECF_PURE 512
+#define ECF_PURE 256
/* Nonzero if this is a call to a function that returns with the stack
pointer depressed. */
-#define ECF_SP_DEPRESSED 1024
+#define ECF_SP_DEPRESSED 512
/* Nonzero if this call is known to always return. */
-#define ECF_ALWAYS_RETURN 2048
+#define ECF_ALWAYS_RETURN 1024
/* Create libcall block around the call. */
-#define ECF_LIBCALL_BLOCK 4096
+#define ECF_LIBCALL_BLOCK 2048
extern int flags_from_decl_or_type (tree);
extern int call_expr_flags (tree);