From 0a1ce58cbde6ce7ca7cc65d0554c218a85236bfe Mon Sep 17 00:00:00 2001 From: John Ravi Date: Sat, 27 Jun 2020 17:21:36 -0700 Subject: [PATCH] adding libcody to lto-wrapper, with some leftover debugging code --- Makefile.in | 6 + Makefile.tpl | 6 + configure | 60 +- configure.ac | 26 + gcc/Makefile.in | 35 +- gcc/config.in | 66 + gcc/configure | 306 ++++- gcc/configure.ac | 137 +++ gcc/cp/Make-lang.in | 2 +- gcc/lto-wrapper.c | 2612 ++++++++++++++++++++-------------------- gcc/mapper-client.cc | 331 +++++ gcc/mapper-resolver.cc | 246 ++++ gcc/mapper-server.cc | 994 +++++++++++++++ gcc/mapper.h | 117 ++ 14 files changed, 3597 insertions(+), 1347 deletions(-) create mode 100644 gcc/mapper-client.cc create mode 100644 gcc/mapper-resolver.cc create mode 100644 gcc/mapper-server.cc create mode 100644 gcc/mapper.h diff --git a/Makefile.in b/Makefile.in index 36e369df6e7e..345ef1b76e2c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -226,6 +226,8 @@ HOST_EXPORTS = \ GMPINC="$(HOST_GMPINC)"; export GMPINC; \ ISLLIBS="$(HOST_ISLLIBS)"; export ISLLIBS; \ ISLINC="$(HOST_ISLINC)"; export ISLINC; \ + CODYLIB="$(HOST_CODYLIB)"; export CODYLIB; \ + CODYLIBINC="$(HOST_CODYLIBINC)"; export CODYLIBINC; \ LIBELFLIBS="$(HOST_LIBELFLIBS)"; export LIBELFLIBS; \ LIBELFINC="$(HOST_LIBELFINC)"; export LIBELFINC; \ XGCC_FLAGS_FOR_TARGET="$(XGCC_FLAGS_FOR_TARGET)"; export XGCC_FLAGS_FOR_TARGET; \ @@ -329,6 +331,10 @@ HOST_GMPINC = @gmpinc@ HOST_ISLLIBS = @isllibs@ HOST_ISLINC = @islinc@ +# Where to find libcody +HOST_CODYLIB = @codylib@ +HOST_CODYLIBINC = @codylibinc@ + # Where to find libelf HOST_LIBELFLIBS = @libelflibs@ HOST_LIBELFINC = @libelfinc@ diff --git a/Makefile.tpl b/Makefile.tpl index efed15117504..6b1a4d723028 100644 --- a/Makefile.tpl +++ b/Makefile.tpl @@ -229,6 +229,8 @@ HOST_EXPORTS = \ GMPINC="$(HOST_GMPINC)"; export GMPINC; \ ISLLIBS="$(HOST_ISLLIBS)"; export ISLLIBS; \ ISLINC="$(HOST_ISLINC)"; export ISLINC; \ + CODYLIB="$(HOST_CODYLIB)"; export CODYLIB; \ + CODYLIBINC="$(HOST_CODYLIBINC)"; export CODYLIBINC; \ LIBELFLIBS="$(HOST_LIBELFLIBS)"; export LIBELFLIBS; \ LIBELFINC="$(HOST_LIBELFINC)"; export LIBELFINC; \ XGCC_FLAGS_FOR_TARGET="$(XGCC_FLAGS_FOR_TARGET)"; export XGCC_FLAGS_FOR_TARGET; \ @@ -332,6 +334,10 @@ HOST_GMPINC = @gmpinc@ HOST_ISLLIBS = @isllibs@ HOST_ISLINC = @islinc@ +# Where to find libcody +HOST_CODYLIB = @codylib@ +HOST_CODYLIBINC = @codylibinc@ + # Where to find libelf HOST_LIBELFLIBS = @libelflibs@ HOST_LIBELFINC = @libelfinc@ diff --git a/configure b/configure index a0c5aca9e8d5..fc2666567335 100755 --- a/configure +++ b/configure @@ -688,6 +688,8 @@ poststage1_ldflags poststage1_libs stage1_ldflags stage1_libs +codylibinc +codylib extra_isl_gmp_configure_flags extra_mpc_mpfr_configure_flags extra_mpc_gmp_configure_flags @@ -755,6 +757,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -806,6 +809,9 @@ with_gmp_dir with_gmp with_gmp_include with_gmp_lib +with_libcody +with_libcody_include +with_libcody_lib with_stage1_libs with_static_standard_libraries with_stage1_ldflags @@ -921,6 +927,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' @@ -1173,6 +1180,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1310,7 +1326,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1470,6 +1486,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1580,6 +1597,14 @@ Optional Packages: --with-gmp-lib=PATH/lib --with-gmp-include=PATH specify directory for installed GMP include files --with-gmp-lib=PATH specify directory for the installed GMP library + --with-libcody=PATH specify prefix directory for installed libcody + package. Equivalent to + --with-libcody-include=PATH/include plus + --with-libcody-lib=PATH/lib + --with-libcody-include=PATH + specify directory for installed libcody include + files + --with-libcody-lib=PATH specify directory for the installed libcody library --with-stage1-libs=LIBS libraries for stage1 --with-static-standard-libraries use -static-libstdc++ and -static-libgcc @@ -6938,6 +6963,39 @@ fi +# Specify a location for libcody + +# Check whether --with-libcody was given. +if test "${with_libcody+set}" = set; then : + withval=$with_libcody; +fi + + +# Check whether --with-libcody-include was given. +if test "${with_libcody_include+set}" = set; then : + withval=$with_libcody_include; +fi + + +# Check whether --with-libcody-lib was given. +if test "${with_libcody_lib+set}" = set; then : + withval=$with_libcody_lib; +fi + + +if test "x$with_libcody" != x; then + codylib="-L$with_libcody/lib" + codylibinc="-I$with_libcody/include" +fi +if test "x$with_libcody_include" != x; then + codylibinc="-I$with_libcody_include" +fi +if test "x$with_libcody_lib" != x; then + codylib="-L$with_libcody_lib" +fi + + + # Libraries to use for stage1 or when not bootstrapping. # Check whether --with-stage1-libs was given. diff --git a/configure.ac b/configure.ac index 1a53ed418e4d..c1a1f2f9305e 100644 --- a/configure.ac +++ b/configure.ac @@ -1708,6 +1708,32 @@ AC_SUBST(extra_mpc_gmp_configure_flags) AC_SUBST(extra_mpc_mpfr_configure_flags) AC_SUBST(extra_isl_gmp_configure_flags) +# Specify a location for libcody +AC_ARG_WITH(libcody, +[AS_HELP_STRING([--with-libcody=PATH], + [specify prefix directory for installed libcody package. + Equivalent to --with-libcody-include=PATH/include + plus --with-libcody-lib=PATH/lib])]) +AC_ARG_WITH(libcody-include, +[AS_HELP_STRING([--with-libcody-include=PATH], + [specify directory for installed libcody include files])]) +AC_ARG_WITH(libcody-lib, +[AS_HELP_STRING([--with-libcody-lib=PATH], + [specify directory for the installed libcody library])]) + +if test "x$with_libcody" != x; then + codylib="-L$with_libcody/lib" + codylibinc="-I$with_libcody/include" +fi +if test "x$with_libcody_include" != x; then + codylibinc="-I$with_libcody_include" +fi +if test "x$with_libcody_lib" != x; then + codylib="-L$with_libcody_lib" +fi +AC_SUBST(codylib) +AC_SUBST(codylibinc) + # Libraries to use for stage1 or when not bootstrapping. AC_ARG_WITH(stage1-libs, [AS_HELP_STRING([--with-stage1-libs=LIBS], [libraries for stage1])], diff --git a/gcc/Makefile.in b/gcc/Makefile.in index d5dcc03d59be..678561762e12 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -388,6 +388,10 @@ GMPINC = @GMPINC@ ISLLIBS = @ISLLIBS@ ISLINC = @ISLINC@ +# How to find libcody +CODYLIB = @CODYLIB@ -lcody +CODYLIBINC = @CODYLIBINC@ + # Set to 'yes' if the LTO front end is enabled. enable_lto = @enable_lto@ @@ -1069,7 +1073,7 @@ BUILD_LIBDEPS= $(BUILD_LIBIBERTY) # and the system's installed libraries. LIBS = @LIBS@ libcommon.a $(CPPLIB) $(LIBINTL) $(LIBICONV) $(LIBBACKTRACE) \ $(LIBIBERTY) $(LIBDECNUMBER) $(HOST_LIBS) -BACKENDLIBS = $(ISLLIBS) $(GMPLIBS) $(PLUGINLIBS) $(HOST_LIBS) \ +BACKENDLIBS = $(ISLLIBS) $(GMPLIBS) $(CODYLIB) $(PLUGINLIBS) $(HOST_LIBS) \ $(ZLIB) $(ZSTD_LIB) # Any system libraries needed just for GNAT. SYSLIBS = @GNAT_LIBEXC@ @@ -1103,7 +1107,7 @@ BUILD_ERRORS = build/errors.o # libintl.h will be found in ../intl if we are using the included libintl. INCLUDES = -I. -I$(@D) -I$(srcdir) -I$(srcdir)/$(@D) \ -I$(srcdir)/../include @INCINTL@ \ - $(CPPINC) $(GMPINC) $(DECNUMINC) $(BACKTRACEINC) \ + $(CPPINC) $(GMPINC) $(CODYLIBINC) $(DECNUMINC) $(BACKTRACEINC) \ $(ISLINC) COMPILE.base = $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) -o $@ @@ -1688,7 +1692,7 @@ ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \ $(OBJS-libcommon-target) main.o c-family/cppspec.o \ $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \ $(GCOV_TOOL_OBJS) $(GENGTYPE_OBJS) gcc-ar.o gcc-nm.o gcc-ranlib.o \ - lto-wrapper.o collect-utils.o + lto-wrapper.o mapper-client.o mapper-resolver.o mapper-server.o collect-utils.o # for anything that is shared use the cc1plus profile data, as that # is likely the most exercised during the build @@ -1763,16 +1767,22 @@ SELFTEST_DEPS = $(GCC_PASSES) stmp-int-hdrs $(srcdir)/testsuite/selftests gnat_install_lib = @gnat_install_lib@ # per-language makefile fragments --include $(LANG_MAKEFRAGS) +ifneq ($(LANG_MAKEFRAGS),) +include $(LANG_MAKEFRAGS) +endif # target and host overrides must follow the per-language makefile fragments # so they can override or augment language-specific variables # target overrides --include $(tmake_file) +ifneq ($(tmake_file),) +include $(tmake_file) +endif # host overrides --include $(xmake_file) +ifneq ($(xmake_file),) +include $(xmake_file) +endif # all-tree.def includes all the tree.def files. all-tree.def: s-alltree; @true @@ -2187,11 +2197,16 @@ collect2$(exeext): $(COLLECT2_OBJS) $(LIBDEPS) CFLAGS-collect2.o += -DTARGET_MACHINE=\"$(target_noncanonical)\" \ @TARGET_SYSTEM_ROOT_DEFINE@ -LTO_WRAPPER_OBJS = lto-wrapper.o collect-utils.o ggc-none.o +LTO_WRAPPER_OBJS = lto-wrapper.o mapper-client.o mapper-resolver.o collect-utils.o ggc-none.o lto-wrapper$(exeext): $(LTO_WRAPPER_OBJS) libcommon-target.a $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o T$@ \ - $(LTO_WRAPPER_OBJS) libcommon-target.a $(LIBS) + $(LTO_WRAPPER_OBJS) libcommon-target.a $(CODYLIB) $(LIBS) mv -f T$@ $@ + +MAPPER_SERVER_OBJS := mapper-server.o mapper-resolver.o +mapper-server$(exeext): $(MAPPER_SERVER_OBJS) $(LIBDEPS) + +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ + $(MAPPER_SERVER_OBJS) version.o $(CODYLIB) $(LIBIBERTY) $(LIBINTL) # Files used by all variants of C or by the stand-alone pre-processor. @@ -3683,6 +3698,10 @@ install-common: native lang.install-common installdirs gcov-dump$(exeext) $(DESTDIR)$(bindir)/$(GCOV_DUMP_INSTALL_NAME)$(exeext); \ fi; \ fi + rm -f $(DESTDIR)$(libexecsubdir)/mapper-server$(exeext) + $(INSTALL_PROGRAM) mapper-server$(exeext) \ + $(DESTDIR)$(libexecsubdir)/mapper-server$(exeext) + # Install the driver program as $(target_noncanonical)-gcc, # $(target_noncanonical)-gcc-$(version), and also as gcc if native. diff --git a/gcc/config.in b/gcc/config.in index 364eba477374..d297dbc09d46 100644 --- a/gcc/config.in +++ b/gcc/config.in @@ -308,6 +308,24 @@ #endif +/* Define if accept4 provided. */ +#ifndef USED_FOR_TARGET +#undef HAVE_ACCEPT4 +#endif + + +/* Define if AF_INET6 supported. */ +#ifndef USED_FOR_TARGET +#undef HAVE_AF_INET6 +#endif + + +/* Define if AF_UNIX supported. */ +#ifndef USED_FOR_TARGET +#undef HAVE_AF_UNIX +#endif + + /* Define if your assembler supports architecture modifiers. */ #ifndef USED_FOR_TARGET #undef HAVE_AS_ARCHITECTURE_MODIFIERS @@ -1161,6 +1179,18 @@ #endif +/* Define if epoll_create, epoll_ctl, epoll_pwait provided. */ +#ifndef USED_FOR_TARGET +#undef HAVE_EPOLL +#endif + + +/* Define to 1 if you have the `execv' function. */ +#ifndef USED_FOR_TARGET +#undef HAVE_EXECV +#endif + + /* Define to 1 if you have the header file. */ #ifndef USED_FOR_TARGET #undef HAVE_EXT_HASH_MAP @@ -1431,6 +1461,12 @@ #endif +/* Define if inet_ntop provided. */ +#ifndef USED_FOR_TARGET +#undef HAVE_INET_NTOP +#endif + + /* Define 0/1 if .init_array/.fini_array sections are available and working. */ #ifndef USED_FOR_TARGET @@ -1695,6 +1731,12 @@ #endif +/* Define to 1 if you have the `memrchr' function. */ +#ifndef USED_FOR_TARGET +#undef HAVE_MEMRCHR +#endif + + /* Define to 1 if you have the `mmap' function. */ #ifndef USED_FOR_TARGET #undef HAVE_MMAP @@ -1738,6 +1780,18 @@ #endif +/* Define to 1 if you have the `posix_fallocate' function. */ +#ifndef USED_FOR_TARGET +#undef HAVE_POSIX_FALLOCATE +#endif + + +/* Define if pselect provided. */ +#ifndef USED_FOR_TARGET +#undef HAVE_PSELECT +#endif + + /* Define to 1 if you have the `putchar_unlocked' function. */ #ifndef USED_FOR_TARGET #undef HAVE_PUTCHAR_UNLOCKED @@ -1750,6 +1804,12 @@ #endif +/* Define if select provided. */ +#ifndef USED_FOR_TARGET +#undef HAVE_SELECT +#endif + + /* Define to 1 if you have the `setlocale' function. */ #ifndef USED_FOR_TARGET #undef HAVE_SETLOCALE @@ -1762,6 +1822,12 @@ #endif +/* Define if defines sighandler_t */ +#ifndef USED_FOR_TARGET +#undef HAVE_SIGHANDLER_T +#endif + + /* Define if the system-provided CRTs are present on Solaris. */ #ifndef USED_FOR_TARGET #undef HAVE_SOLARIS_CRTS diff --git a/gcc/configure b/gcc/configure index f224679ed3ee..6a69799836d6 100755 --- a/gcc/configure +++ b/gcc/configure @@ -639,6 +639,8 @@ PICFLAG enable_host_shared enable_plugin pluginlibs +CODYLIBINC +CODYLIB ISLINC ISLLIBS GMPINC @@ -1038,7 +1040,9 @@ CPP GMPLIBS GMPINC ISLLIBS -ISLINC' +ISLINC +CODYLIB +CODYLIBINC' # Initialize some variables set by options. @@ -1866,6 +1870,8 @@ Some influential environment variables: GMPINC How to find GMP include files ISLLIBS How to link isl ISLINC How to find isl include files + CODYLIB How to link Cody + CODYLIBINC How to find Cody include files Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -10200,6 +10206,14 @@ _ACEOF fi +ac_fn_cxx_check_type "$LINENO" "sighander_t" "ac_cv_type_sighander_t" "signal.h +" +if test "x$ac_cv_type_sighander_t" = xyes; then : + +$as_echo "#define HAVE_SIGHANDLER_T 1" >>confdefs.h + +fi + ac_fn_cxx_check_header_preproc "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" @@ -11923,6 +11937,296 @@ $as_echo "#define HOST_HAS_F_SETLKW 1" >>confdefs.h fi +# C++ Modules would like some networking features to provide the mapping +# server. You can still use modules without them though. +# The following network-related checks could probably do with some +# Windows and other non-linux defenses and checking. + +# Local socket connectivity wants AF_UNIX networking +# Check for AF_UNIX networking +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for AF_UNIX" >&5 +$as_echo_n "checking for AF_UNIX... " >&6; } +if ${ac_cv_af_unix+:} false; then : + $as_echo_n "(cached) " >&6 +else + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +#include +int +main () +{ + +sockaddr_un un; +un.sun_family = AF_UNSPEC; +int fd = socket (AF_UNIX, SOCK_STREAM, 0); +connect (fd, (sockaddr *)&un, sizeof (un)); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_af_unix=yes +else + ac_cv_af_unix=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_af_unix" >&5 +$as_echo "$ac_cv_af_unix" >&6; } +if test $ac_cv_af_unix = yes; then + +$as_echo "#define HAVE_AF_UNIX 1" >>confdefs.h + +fi + +# Remote socket connectivity wants AF_INET6 networking +# Check for AF_INET6 networking +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for AF_INET6" >&5 +$as_echo_n "checking for AF_INET6... " >&6; } +if ${ac_cv_af_inet6+:} false; then : + $as_echo_n "(cached) " >&6 +else + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +#include +int +main () +{ + +sockaddr_in6 in6; +in6.sin6_family = AF_UNSPEC; +struct addrinfo *addrs = 0; +struct addrinfo hints; +hints.ai_flags = 0; +hints.ai_family = AF_INET6; +hints.ai_socktype = SOCK_STREAM; +hints.ai_protocol = 0; +hints.ai_canonname = 0; +hints.ai_addr = 0; +hints.ai_next = 0; +int e = getaddrinfo ("localhost", 0, &hints, &addrs); +const char *str = gai_strerror (e); +freeaddrinfo (addrs); +int fd = socket (AF_INET6, SOCK_STREAM, 0); +connect (fd, (sockaddr *)&in6, sizeof (in6)); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_af_inet6=yes +else + ac_cv_af_inet6=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_af_inet6" >&5 +$as_echo "$ac_cv_af_inet6" >&6; } +if test $ac_cv_af_inet6 = yes; then + +$as_echo "#define HAVE_AF_INET6 1" >>confdefs.h + +fi + +# Efficient server response wants epoll +# Check for epoll_create, epoll_ctl, epoll_pwait +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for epoll" >&5 +$as_echo_n "checking for epoll... " >&6; } +if ${ac_cv_epoll+:} false; then : + $as_echo_n "(cached) " >&6 +else + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +int +main () +{ + +int fd = epoll_create (1); +epoll_event ev; +ev.events = EPOLLIN; +ev.data.fd = 0; +epoll_ctl (fd, EPOLL_CTL_ADD, 0, &ev); +epoll_pwait (fd, 0, 0, -1, 0); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_epoll=yes +else + ac_cv_epoll=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_epoll" >&5 +$as_echo "$ac_cv_epoll" >&6; } +if test $ac_cv_epoll = yes; then + +$as_echo "#define HAVE_EPOLL 1" >>confdefs.h + +fi + +# If we can't use epoll, try pselect. +# Check for pselect +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pselect" >&5 +$as_echo_n "checking for pselect... " >&6; } +if ${ac_cv_pselect+:} false; then : + $as_echo_n "(cached) " >&6 +else + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +int +main () +{ + +pselect (0, 0, 0, 0, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_pselect=yes +else + ac_cv_pselect=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_pselect" >&5 +$as_echo "$ac_cv_pselect" >&6; } +if test $ac_cv_pselect = yes; then + +$as_echo "#define HAVE_PSELECT 1" >>confdefs.h + +fi + +# And failing that, use good old select. +# If we can't even use this, the server is serialized. +# Check for select +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for select" >&5 +$as_echo_n "checking for select... " >&6; } +if ${ac_cv_select+:} false; then : + $as_echo_n "(cached) " >&6 +else + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +int +main () +{ + +select (0, 0, 0, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_select=yes +else + ac_cv_select=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_select" >&5 +$as_echo "$ac_cv_select" >&6; } +if test $ac_cv_select = yes; then + +$as_echo "#define HAVE_SELECT 1" >>confdefs.h + +fi + +# Avoid some fnctl calls by using accept4, when available. +# Check for accept4 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for accept4" >&5 +$as_echo_n "checking for accept4... " >&6; } +if ${ac_cv_accept4+:} false; then : + $as_echo_n "(cached) " >&6 +else + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +int +main () +{ + +int err = accept4 (1, 0, 0, SOCK_NONBLOCK); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_accept4=yes +else + ac_cv_accept4=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_accept4" >&5 +$as_echo "$ac_cv_accept4" >&6; } +if test $ac_cv_accept4 = yes; then + +$as_echo "#define HAVE_ACCEPT4 1" >>confdefs.h + +fi + +# For better server messages, look for a way to stringize network addresses +# Check for inet_ntop +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntop" >&5 +$as_echo_n "checking for inet_ntop... " >&6; } +if ${ac_cv_inet_ntop+:} false; then : + $as_echo_n "(cached) " >&6 +else + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int +main () +{ + +sockaddr_in6 in6; +char buf[INET6_ADDRSTRLEN]; +const char *str = inet_ntop (AF_INET6, &in6, buf, sizeof (buf)); + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_inet_ntop=yes +else + ac_cv_inet_ntop=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_inet_ntop" >&5 +$as_echo "$ac_cv_inet_ntop" >&6; } +if test $ac_cv_inet_ntop = yes; then + +$as_echo "#define HAVE_INET_NTOP 1" >>confdefs.h + +fi + # Restore CFLAGS, CXXFLAGS from before the gcc_AC_NEED_DECLARATIONS tests. CFLAGS="$saved_CFLAGS" CXXFLAGS="$saved_CXXFLAGS" diff --git a/gcc/configure.ac b/gcc/configure.ac index e83f0833ef3f..81b6cb70d2c0 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -1439,6 +1439,10 @@ fi AC_CHECK_TYPE(ssize_t, int) AC_CHECK_TYPE(caddr_t, char *) +AC_CHECK_TYPE(sighander_t, + AC_DEFINE(HAVE_SIGHANDLER_T, 1, + [Define if defines sighandler_t]), + ,signal.h) GCC_AC_FUNC_MMAP_BLACKLIST @@ -1584,6 +1588,136 @@ if test $ac_cv_f_setlkw = yes; then [Define if F_SETLKW supported by fcntl.]) fi +# C++ Modules would like some networking features to provide the mapping +# server. You can still use modules without them though. +# The following network-related checks could probably do with some +# Windows and other non-linux defenses and checking. +# C++ LTO will take advantage of these networking features too. + +# Local socket connectivity wants AF_UNIX networking +# Check for AF_UNIX networking +AC_CACHE_CHECK(for AF_UNIX, ac_cv_af_unix, [ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include ]],[[ +sockaddr_un un; +un.sun_family = AF_UNSPEC; +int fd = socket (AF_UNIX, SOCK_STREAM, 0); +connect (fd, (sockaddr *)&un, sizeof (un));]])], +[ac_cv_af_unix=yes], +[ac_cv_af_unix=no])]) +if test $ac_cv_af_unix = yes; then + AC_DEFINE(HAVE_AF_UNIX, 1, + [Define if AF_UNIX supported.]) +fi + +# Remote socket connectivity wants AF_INET6 networking +# Check for AF_INET6 networking +AC_CACHE_CHECK(for AF_INET6, ac_cv_af_inet6, [ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include ]],[[ +sockaddr_in6 in6; +in6.sin6_family = AF_UNSPEC; +struct addrinfo *addrs = 0; +struct addrinfo hints; +hints.ai_flags = 0; +hints.ai_family = AF_INET6; +hints.ai_socktype = SOCK_STREAM; +hints.ai_protocol = 0; +hints.ai_canonname = 0; +hints.ai_addr = 0; +hints.ai_next = 0; +int e = getaddrinfo ("localhost", 0, &hints, &addrs); +const char *str = gai_strerror (e); +freeaddrinfo (addrs); +int fd = socket (AF_INET6, SOCK_STREAM, 0); +connect (fd, (sockaddr *)&in6, sizeof (in6));]])], +[ac_cv_af_inet6=yes], +[ac_cv_af_inet6=no])]) +if test $ac_cv_af_inet6 = yes; then + AC_DEFINE(HAVE_AF_INET6, 1, + [Define if AF_INET6 supported.]) +fi + +# Efficient server response wants epoll +# Check for epoll_create, epoll_ctl, epoll_pwait +AC_CACHE_CHECK(for epoll, ac_cv_epoll, [ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include ]],[[ +int fd = epoll_create (1); +epoll_event ev; +ev.events = EPOLLIN; +ev.data.fd = 0; +epoll_ctl (fd, EPOLL_CTL_ADD, 0, &ev); +epoll_pwait (fd, 0, 0, -1, 0);]])], +[ac_cv_epoll=yes], +[ac_cv_epoll=no])]) +if test $ac_cv_epoll = yes; then + AC_DEFINE(HAVE_EPOLL, 1, + [Define if epoll_create, epoll_ctl, epoll_pwait provided.]) +fi + +# If we can't use epoll, try pselect. +# Check for pselect +AC_CACHE_CHECK(for pselect, ac_cv_pselect, [ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include ]],[[ +pselect (0, 0, 0, 0, 0, 0);]])], +[ac_cv_pselect=yes], +[ac_cv_pselect=no])]) +if test $ac_cv_pselect = yes; then + AC_DEFINE(HAVE_PSELECT, 1, + [Define if pselect provided.]) +fi + +# And failing that, use good old select. +# If we can't even use this, the server is serialized. +# Check for select +AC_CACHE_CHECK(for select, ac_cv_select, [ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include ]],[[ +select (0, 0, 0, 0, 0);]])], +[ac_cv_select=yes], +[ac_cv_select=no])]) +if test $ac_cv_select = yes; then + AC_DEFINE(HAVE_SELECT, 1, + [Define if select provided.]) +fi + +# Avoid some fnctl calls by using accept4, when available. +# Check for accept4 +AC_CACHE_CHECK(for accept4, ac_cv_accept4, [ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include ]],[[ +int err = accept4 (1, 0, 0, SOCK_NONBLOCK);]])], +[ac_cv_accept4=yes], +[ac_cv_accept4=no])]) +if test $ac_cv_accept4 = yes; then + AC_DEFINE(HAVE_ACCEPT4, 1, + [Define if accept4 provided.]) +fi + +# For better server messages, look for a way to stringize network addresses +# Check for inet_ntop +AC_CACHE_CHECK(for inet_ntop, ac_cv_inet_ntop, [ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include ]],[[ +sockaddr_in6 in6; +char buf[INET6_ADDRSTRLEN]; +const char *str = inet_ntop (AF_INET6, &in6, buf, sizeof (buf));]])], +[ac_cv_inet_ntop=yes], +[ac_cv_inet_ntop=no])]) +if test $ac_cv_inet_ntop = yes; then + AC_DEFINE(HAVE_INET_NTOP, 1, + [Define if inet_ntop provided.]) +fi + # Restore CFLAGS, CXXFLAGS from before the gcc_AC_NEED_DECLARATIONS tests. CFLAGS="$saved_CFLAGS" CXXFLAGS="$saved_CXXFLAGS" @@ -6698,6 +6832,9 @@ if test "x${ISLLIBS}" != "x" ; then AC_DEFINE(HAVE_isl, 1, [Define if isl is in use.]) fi +AC_ARG_VAR(CODYLIB,[How to link Cody]) +AC_ARG_VAR(CODYLIBINC,[How to find Cody include files]) + GCC_ENABLE_PLUGINS AC_SUBST(pluginlibs) AC_SUBST(enable_plugin) diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index 7896591dd4b2..41de295ef9cc 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -46,7 +46,7 @@ CP_PLUGIN_HEADERS := cp-tree.h cxx-pretty-print.h name-lookup.h type-utils.h ope # Note that it would be nice to move the dependency on g++ # into the C++ rule, but that needs a little bit of work # to do the right thing within all.cross. -c++: cc1plus$(exeext) +c++: cc1plus$(exeext) mapper-server$(exeext) # Tell GNU make to ignore these if they exist. .PHONY: c++ diff --git a/gcc/lto-wrapper.c b/gcc/lto-wrapper.c index 939a83ac73a4..ac77e537e19b 100644 --- a/gcc/lto-wrapper.c +++ b/gcc/lto-wrapper.c @@ -3,21 +3,21 @@ Factored out of collect2 by Rafael Espindola -This file is part of GCC. + This file is part of GCC. -GCC is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 3, or (at your option) any later -version. + GCC is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3, or (at your option) any later + version. -GCC is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. + GCC is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -. */ + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + . */ /* This program is passed a gcc, a list of gcc arguments and a list of @@ -25,17 +25,16 @@ along with GCC; see the file COPYING3. If not see we are in whopr mode or not modifies the arguments and needed and prints a list of output files on stdout. - Example: +Example: +$ lto-wrapper gcc/xgcc -B gcc a.o b.o -o test -flto - $ lto-wrapper gcc/xgcc -B gcc a.o b.o -o test -flto +The above will print something like +/tmp/ccwbQ8B2.lto.o - The above will print something like - /tmp/ccwbQ8B2.lto.o - - If WHOPR is used instead, more than one file might be produced - ./ccXj2DTk.lto.ltrans.o - ./ccCJuXGv.lto.ltrans.o -*/ +If WHOPR is used instead, more than one file might be produced +./ccXj2DTk.lto.ltrans.o +./ccCJuXGv.lto.ltrans.o + */ #include "config.h" #include "system.h" @@ -48,6 +47,11 @@ along with GCC; see the file COPYING3. If not see #include "simple-object.h" #include "lto-section-names.h" #include "collect-utils.h" +#include +#include + +#define MAPPER_FOR_GCC 1 +#include "mapper.h" /* Environment variable, used for passing the names of offload targets from GCC driver to lto-wrapper. */ @@ -84,11 +88,48 @@ static bool xassembler_options_error = false; const char tool_name[] = "lto-wrapper"; -/* Delete tempfiles. Called from utils_cleanup. */ +/********************************************************************/ + +// FIXME: Put in header somewhere, and maybe verify value +static const location_t main_source_loc = 32; + +/* Our module mapper (created lazily). */ +module_client *mapper; + +static module_client *make_mapper (location_t loc); +inline module_client *get_mapper (location_t loc) { + auto *res = mapper; + if (!res) + res = make_mapper (loc); + return res; +} + +/********************************************************************/ + +/* Create a new mapper connecting to OPTION. */ -void -tool_cleanup (bool) +module_client * +make_mapper (location_t loc) { + //const char *option = module_mapper_name; + //if (!option) + // option = getenv ("CXX_MODULE_MAPPER"); + + const char *option = getenv ("CXX_MODULE_MAPPER"); + fprintf(stderr, "\tCXX_MODULE_MAPPER option: %s\n", option); + + //mapper = module_client::open_module_client(loc, option, &set_cmi_repo, (save_decoded_options[0].opt_index == OPT_SPECIAL_program_name) && save_decoded_options[0].arg != progname ? save_decoded_options[0].arg : nullptr); + + fprintf(stderr, "\tcalling module_client constructor\n"); + mapper = module_client::open_module_client(loc, option, nullptr, nullptr); + + return mapper; +} + + +/* Delete tempfiles. Called from utils_cleanup. */ + +void tool_cleanup (bool) { unsigned int i; if (ltrans_output_file) @@ -102,32 +143,25 @@ tool_cleanup (bool) if (early_debug_object_names) for (i = 0; i < num_deb_objs; ++i) if (early_debug_object_names[i]) - maybe_unlink (early_debug_object_names[i]); - for (i = 0; i < nr; ++i) - { - maybe_unlink (input_names[i]); - if (output_names[i]) - maybe_unlink (output_names[i]); - } + maybe_unlink (early_debug_object_names[i]); + for (i = 0; i < nr; ++i) { + maybe_unlink (input_names[i]); + if (output_names[i]) + maybe_unlink (output_names[i]); + } } -static void -lto_wrapper_cleanup (void) -{ +static void lto_wrapper_cleanup (void) { utils_cleanup (false); } /* Unlink a temporary LTRANS file unless requested otherwise. */ - -void -maybe_unlink (const char *file) -{ - if (!save_temps) - { - if (unlink_if_ordinary (file) - && errno != ENOENT) - fatal_error (input_location, "deleting LTRANS file %s: %m", file); - } +void maybe_unlink (const char *file) { + if (!save_temps) { + if (unlink_if_ordinary (file) + && errno != ENOENT) + fatal_error (input_location, "deleting LTRANS file %s: %m", file); + } else if (verbose) fprintf (stderr, "[Leaving LTRANS %s]\n", file); } @@ -138,12 +172,10 @@ maybe_unlink (const char *file) /* Create decoded options from the COLLECT_GCC and COLLECT_GCC_OPTIONS environment. */ -static void -get_options_from_collect_gcc_options (const char *collect_gcc, - const char *collect_gcc_options, - struct cl_decoded_option **decoded_options, - unsigned int *decoded_options_count) -{ +static void get_options_from_collect_gcc_options (const char *collect_gcc, + const char *collect_gcc_options, struct cl_decoded_option **decoded_options, + unsigned int *decoded_options_count) { + struct obstack argv_obstack; const char **argv; int argc; @@ -152,55 +184,50 @@ get_options_from_collect_gcc_options (const char *collect_gcc, obstack_ptr_grow (&argv_obstack, collect_gcc); parse_options_from_collect_gcc_options (collect_gcc_options, - &argv_obstack, &argc); + &argv_obstack, &argc); argv = XOBFINISH (&argv_obstack, const char **); decode_cmdline_options_to_array (argc, (const char **)argv, CL_DRIVER, - decoded_options, decoded_options_count); + decoded_options, decoded_options_count); obstack_free (&argv_obstack, NULL); } /* Append OPTION to the options array DECODED_OPTIONS with size DECODED_OPTIONS_COUNT. */ -static void -append_option (struct cl_decoded_option **decoded_options, - unsigned int *decoded_options_count, - struct cl_decoded_option *option) -{ +static void append_option (struct cl_decoded_option **decoded_options, + unsigned int *decoded_options_count, struct cl_decoded_option *option) { + ++*decoded_options_count; *decoded_options = (struct cl_decoded_option *) - xrealloc (*decoded_options, - (*decoded_options_count - * sizeof (struct cl_decoded_option))); + xrealloc (*decoded_options, + (*decoded_options_count + * sizeof (struct cl_decoded_option))); memcpy (&(*decoded_options)[*decoded_options_count - 1], option, - sizeof (struct cl_decoded_option)); + sizeof (struct cl_decoded_option)); } /* Remove option number INDEX from DECODED_OPTIONS, update DECODED_OPTIONS_COUNT. */ -static void -remove_option (struct cl_decoded_option **decoded_options, - int index, unsigned int *decoded_options_count) -{ +static void remove_option (struct cl_decoded_option **decoded_options, + int index, unsigned int *decoded_options_count) { + --*decoded_options_count; memmove (&(*decoded_options)[index + 1], - &(*decoded_options)[index], - sizeof (struct cl_decoded_option) - * (*decoded_options_count - index)); + &(*decoded_options)[index], + sizeof (struct cl_decoded_option) + * (*decoded_options_count - index)); } /* Try to merge and complain about options FDECODED_OPTIONS when applied ontop of DECODED_OPTIONS. */ -static void -merge_and_complain (struct cl_decoded_option **decoded_options, - unsigned int *decoded_options_count, - struct cl_decoded_option *fdecoded_options, - unsigned int fdecoded_options_count) -{ +static void merge_and_complain (struct cl_decoded_option **decoded_options, + unsigned int *decoded_options_count, struct cl_decoded_option *fdecoded_options, + unsigned int fdecoded_options_count) { + unsigned int i, j; struct cl_decoded_option *pic_option = NULL; struct cl_decoded_option *pie_option = NULL; @@ -220,334 +247,313 @@ merge_and_complain (struct cl_decoded_option **decoded_options, /* The following does what the old LTO option code did, union all target and a selected set of common options. */ - for (i = 0; i < fdecoded_options_count; ++i) - { - struct cl_decoded_option *foption = &fdecoded_options[i]; - switch (foption->opt_index) - { - case OPT_SPECIAL_unknown: - case OPT_SPECIAL_ignore: - case OPT_SPECIAL_warn_removed: - case OPT_SPECIAL_program_name: - case OPT_SPECIAL_input_file: - break; - - default: - if (!(cl_options[foption->opt_index].flags & CL_TARGET)) - break; - - /* Fallthru. */ - case OPT_fdiagnostics_show_caret: - case OPT_fdiagnostics_show_labels: - case OPT_fdiagnostics_show_line_numbers: - case OPT_fdiagnostics_show_option: - case OPT_fdiagnostics_show_location_: - case OPT_fshow_column: - case OPT_fcommon: - case OPT_fgnu_tm: - case OPT_g: - /* Do what the old LTO code did - collect exactly one option - setting per OPT code, we pick the first we encounter. - ??? This doesn't make too much sense, but when it doesn't - then we should complain. */ - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - break; - - /* Figure out what PIC/PIE level wins and merge the results. */ - case OPT_fPIC: - case OPT_fpic: - pic_option = foption; - break; - case OPT_fPIE: - case OPT_fpie: - pie_option = foption; - break; - - case OPT_fopenmp: - case OPT_fopenacc: - /* For selected options we can merge conservatively. */ - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - /* -fopenmp > -fno-openmp, - -fopenacc > -fno-openacc */ - else if (foption->value > (*decoded_options)[j].value) - (*decoded_options)[j] = *foption; - break; - - case OPT_fopenacc_dim_: - /* Append or check identical. */ - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - else if (strcmp ((*decoded_options)[j].arg, foption->arg)) - fatal_error (input_location, - "option %s with different values", - foption->orig_option_with_args_text); - break; - - case OPT_O: - case OPT_Ofast: - case OPT_Og: - case OPT_Os: - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == OPT_O - || (*decoded_options)[j].opt_index == OPT_Ofast - || (*decoded_options)[j].opt_index == OPT_Og - || (*decoded_options)[j].opt_index == OPT_Os) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - else if ((*decoded_options)[j].opt_index == foption->opt_index - && foption->opt_index != OPT_O) - /* Exact same options get merged. */ - ; - else - { - /* For mismatched option kinds preserve the optimization - level only, thus merge it as -On. This also handles - merging of same optimization level -On. */ - int level = 0; - switch (foption->opt_index) - { - case OPT_O: - if (foption->arg[0] == '\0') - level = MAX (level, 1); - else - level = MAX (level, atoi (foption->arg)); - break; - case OPT_Ofast: - level = MAX (level, 3); - break; - case OPT_Og: - level = MAX (level, 1); - break; - case OPT_Os: - level = MAX (level, 2); - break; - default: - gcc_unreachable (); - } - switch ((*decoded_options)[j].opt_index) - { - case OPT_O: - if ((*decoded_options)[j].arg[0] == '\0') - level = MAX (level, 1); - else - level = MAX (level, atoi ((*decoded_options)[j].arg)); - break; - case OPT_Ofast: - level = MAX (level, 3); - break; - case OPT_Og: - level = MAX (level, 1); - break; - case OPT_Os: - level = MAX (level, 2); - break; - default: - gcc_unreachable (); - } - (*decoded_options)[j].opt_index = OPT_O; - char *tem; - tem = xasprintf ("-O%d", level); - (*decoded_options)[j].arg = &tem[2]; - (*decoded_options)[j].canonical_option[0] = tem; - (*decoded_options)[j].value = 1; - } - break; - - - case OPT_foffload_abi_: - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - else if (foption->value != (*decoded_options)[j].value) - fatal_error (input_location, - "option %s not used consistently in all LTO input" - " files", foption->orig_option_with_args_text); - break; - - - case OPT_foffload_: - append_option (decoded_options, decoded_options_count, foption); - break; - } + for (i = 0; i < fdecoded_options_count; ++i) { + struct cl_decoded_option *foption = &fdecoded_options[i]; + switch (foption->opt_index) { + case OPT_SPECIAL_unknown: + case OPT_SPECIAL_ignore: + case OPT_SPECIAL_warn_removed: + case OPT_SPECIAL_program_name: + case OPT_SPECIAL_input_file: + break; + + default: + if (!(cl_options[foption->opt_index].flags & CL_TARGET)) + break; + + /* Fallthru. */ + case OPT_fdiagnostics_show_caret: + case OPT_fdiagnostics_show_labels: + case OPT_fdiagnostics_show_line_numbers: + case OPT_fdiagnostics_show_option: + case OPT_fdiagnostics_show_location_: + case OPT_fshow_column: + case OPT_fcommon: + case OPT_fgnu_tm: + case OPT_g: + /* Do what the old LTO code did - collect exactly one option + setting per OPT code, we pick the first we encounter. + ??? This doesn't make too much sense, but when it doesn't + then we should complain. */ + for (j = 0; j < *decoded_options_count; ++j) + if ((*decoded_options)[j].opt_index == foption->opt_index) + break; + if (j == *decoded_options_count) + append_option (decoded_options, decoded_options_count, foption); + break; + + /* Figure out what PIC/PIE level wins and merge the results. */ + case OPT_fPIC: + case OPT_fpic: + pic_option = foption; + break; + case OPT_fPIE: + case OPT_fpie: + pie_option = foption; + break; + + case OPT_fopenmp: + case OPT_fopenacc: + /* For selected options we can merge conservatively. */ + for (j = 0; j < *decoded_options_count; ++j) + if ((*decoded_options)[j].opt_index == foption->opt_index) + break; + if (j == *decoded_options_count) + append_option (decoded_options, decoded_options_count, foption); + /* -fopenmp > -fno-openmp, + -fopenacc > -fno-openacc */ + else if (foption->value > (*decoded_options)[j].value) + (*decoded_options)[j] = *foption; + break; + + case OPT_fopenacc_dim_: + /* Append or check identical. */ + for (j = 0; j < *decoded_options_count; ++j) + if ((*decoded_options)[j].opt_index == foption->opt_index) + break; + if (j == *decoded_options_count) + append_option (decoded_options, decoded_options_count, foption); + else if (strcmp ((*decoded_options)[j].arg, foption->arg)) + fatal_error (input_location, + "option %s with different values", + foption->orig_option_with_args_text); + break; + + case OPT_O: + case OPT_Ofast: + case OPT_Og: + case OPT_Os: + for (j = 0; j < *decoded_options_count; ++j) + if ((*decoded_options)[j].opt_index == OPT_O + || (*decoded_options)[j].opt_index == OPT_Ofast + || (*decoded_options)[j].opt_index == OPT_Og + || (*decoded_options)[j].opt_index == OPT_Os) + break; + if (j == *decoded_options_count) + append_option (decoded_options, decoded_options_count, foption); + else if ((*decoded_options)[j].opt_index == foption->opt_index + && foption->opt_index != OPT_O) + /* Exact same options get merged. */ + ; + else { + /* For mismatched option kinds preserve the optimization + level only, thus merge it as -On. This also handles + merging of same optimization level -On. */ + int level = 0; + switch (foption->opt_index) { + case OPT_O: + if (foption->arg[0] == '\0') + level = MAX (level, 1); + else + level = MAX (level, atoi (foption->arg)); + break; + case OPT_Ofast: + level = MAX (level, 3); + break; + case OPT_Og: + level = MAX (level, 1); + break; + case OPT_Os: + level = MAX (level, 2); + break; + default: + gcc_unreachable (); + } + switch ((*decoded_options)[j].opt_index) { + case OPT_O: + if ((*decoded_options)[j].arg[0] == '\0') + level = MAX (level, 1); + else + level = MAX (level, atoi ((*decoded_options)[j].arg)); + break; + case OPT_Ofast: + level = MAX (level, 3); + break; + case OPT_Og: + level = MAX (level, 1); + break; + case OPT_Os: + level = MAX (level, 2); + break; + default: + gcc_unreachable (); + } + (*decoded_options)[j].opt_index = OPT_O; + char *tem; + tem = xasprintf ("-O%d", level); + (*decoded_options)[j].arg = &tem[2]; + (*decoded_options)[j].canonical_option[0] = tem; + (*decoded_options)[j].value = 1; + } + break; + + + case OPT_foffload_abi_: + for (j = 0; j < *decoded_options_count; ++j) + if ((*decoded_options)[j].opt_index == foption->opt_index) + break; + if (j == *decoded_options_count) + append_option (decoded_options, decoded_options_count, foption); + else if (foption->value != (*decoded_options)[j].value) + fatal_error (input_location, + "option %s not used consistently in all LTO input" + " files", foption->orig_option_with_args_text); + break; + + + case OPT_foffload_: + append_option (decoded_options, decoded_options_count, foption); + break; } + } /* Merge PIC options: - -fPIC + -fpic = -fpic - -fPIC + -fno-pic = -fno-pic - -fpic/-fPIC + nothing = nothing. + -fPIC + -fpic = -fpic + -fPIC + -fno-pic = -fno-pic + -fpic/-fPIC + nothing = nothing. It is a common mistake to mix few -fPIC compiled objects into otherwise non-PIC code. We do not want to build everything with PIC then. Similarly we merge PIE options, however in addition we keep - -fPIC + -fPIE = -fPIE - -fpic + -fPIE = -fpie - -fPIC/-fpic + -fpie = -fpie + -fPIC + -fPIE = -fPIE + -fpic + -fPIE = -fpie + -fPIC/-fpic + -fpie = -fpie It would be good to warn on mismatches, but it is bit hard to do as we do not know what nothing translates to. */ - + for (unsigned int j = 0; j < *decoded_options_count;) if ((*decoded_options)[j].opt_index == OPT_fPIC - || (*decoded_options)[j].opt_index == OPT_fpic) - { - /* -fno-pic in one unit implies -fno-pic everywhere. */ - if ((*decoded_options)[j].value == 0) - j++; - /* If we have no pic option or merge in -fno-pic, we still may turn - existing pic/PIC mode into pie/PIE if -fpie/-fPIE is present. */ - else if ((pic_option && pic_option->value == 0) - || !pic_option) - { - if (pie_option) - { - bool big = (*decoded_options)[j].opt_index == OPT_fPIC - && pie_option->opt_index == OPT_fPIE; - (*decoded_options)[j].opt_index = big ? OPT_fPIE : OPT_fpie; - if (pie_option->value) - (*decoded_options)[j].canonical_option[0] - = big ? "-fPIE" : "-fpie"; - else - (*decoded_options)[j].canonical_option[0] = "-fno-pie"; - (*decoded_options)[j].value = pie_option->value; - j++; - } - else if (pic_option) - { - (*decoded_options)[j] = *pic_option; - j++; - } - /* We do not know if target defaults to pic or not, so just remove - option if it is missing in one unit but enabled in other. */ - else - remove_option (decoded_options, j, decoded_options_count); - } - else if (pic_option->opt_index == OPT_fpic - && (*decoded_options)[j].opt_index == OPT_fPIC) - { - (*decoded_options)[j] = *pic_option; - j++; - } - else - j++; + || (*decoded_options)[j].opt_index == OPT_fpic) { + + /* -fno-pic in one unit implies -fno-pic everywhere. */ + if ((*decoded_options)[j].value == 0) + j++; + /* If we have no pic option or merge in -fno-pic, we still may turn + existing pic/PIC mode into pie/PIE if -fpie/-fPIE is present. */ + else if ((pic_option && pic_option->value == 0) || !pic_option) { + if (pie_option) { + bool big = (*decoded_options)[j].opt_index == OPT_fPIC + && pie_option->opt_index == OPT_fPIE; + (*decoded_options)[j].opt_index = big ? OPT_fPIE : OPT_fpie; + if (pie_option->value) + (*decoded_options)[j].canonical_option[0] = big ? "-fPIE" : "-fpie"; + else + (*decoded_options)[j].canonical_option[0] = "-fno-pie"; + (*decoded_options)[j].value = pie_option->value; + j++; + } + else if (pic_option) { + (*decoded_options)[j] = *pic_option; + j++; + } + /* We do not know if target defaults to pic or not, so just remove + option if it is missing in one unit but enabled in other. */ + else + remove_option (decoded_options, j, decoded_options_count); + } + else if (pic_option->opt_index == OPT_fpic && + (*decoded_options)[j].opt_index == OPT_fPIC) { + + (*decoded_options)[j] = *pic_option; + j++; } - else if ((*decoded_options)[j].opt_index == OPT_fPIE - || (*decoded_options)[j].opt_index == OPT_fpie) + else + j++; + } + else if ((*decoded_options)[j].opt_index == OPT_fPIE + || (*decoded_options)[j].opt_index == OPT_fpie) { + /* -fno-pie in one unit implies -fno-pie everywhere. */ + if ((*decoded_options)[j].value == 0) + j++; + /* If we have no pie option or merge in -fno-pie, we still preserve + PIE/pie if pic/PIC is present. */ + else if ((pie_option && pie_option->value == 0) || !pie_option) { + /* If -fPIC/-fpic is given, merge it with -fPIE/-fpie. */ + if (pic_option) { + if (pic_option->opt_index == OPT_fpic && + (*decoded_options)[j].opt_index == OPT_fPIE) { + + (*decoded_options)[j].opt_index = OPT_fpie; + (*decoded_options)[j].canonical_option[0] = pic_option->value ? "-fpie" : "-fno-pie"; + } + else if (!pic_option->value) + (*decoded_options)[j].canonical_option[0] = "-fno-pie"; + (*decoded_options)[j].value = pic_option->value; + j++; + } + else if (pie_option) + { + (*decoded_options)[j] = *pie_option; + j++; + } + /* Because we always append pic/PIE options this code path should + not happen unless the LTO object was built by old lto1 which + did not contain that logic yet. */ + else + remove_option (decoded_options, j, decoded_options_count); + } + else if (pie_option->opt_index == OPT_fpie + && (*decoded_options)[j].opt_index == OPT_fPIE) { - /* -fno-pie in one unit implies -fno-pie everywhere. */ - if ((*decoded_options)[j].value == 0) - j++; - /* If we have no pie option or merge in -fno-pie, we still preserve - PIE/pie if pic/PIC is present. */ - else if ((pie_option && pie_option->value == 0) - || !pie_option) - { - /* If -fPIC/-fpic is given, merge it with -fPIE/-fpie. */ - if (pic_option) - { - if (pic_option->opt_index == OPT_fpic - && (*decoded_options)[j].opt_index == OPT_fPIE) - { - (*decoded_options)[j].opt_index = OPT_fpie; - (*decoded_options)[j].canonical_option[0] - = pic_option->value ? "-fpie" : "-fno-pie"; - } - else if (!pic_option->value) - (*decoded_options)[j].canonical_option[0] = "-fno-pie"; - (*decoded_options)[j].value = pic_option->value; - j++; - } - else if (pie_option) - { - (*decoded_options)[j] = *pie_option; - j++; - } - /* Because we always append pic/PIE options this code path should - not happen unless the LTO object was built by old lto1 which - did not contain that logic yet. */ - else - remove_option (decoded_options, j, decoded_options_count); - } - else if (pie_option->opt_index == OPT_fpie - && (*decoded_options)[j].opt_index == OPT_fPIE) - { - (*decoded_options)[j] = *pie_option; - j++; - } - else - j++; + (*decoded_options)[j] = *pie_option; + j++; } - else - j++; + else + j++; + } + else + j++; if (!xassembler_options_error) for (i = j = 0; ; i++, j++) - { - for (; i < *decoded_options_count; i++) - if ((*decoded_options)[i].opt_index == OPT_Xassembler) - break; - - for (; j < fdecoded_options_count; j++) - if (fdecoded_options[j].opt_index == OPT_Xassembler) - break; - - if (i == *decoded_options_count && j == fdecoded_options_count) - break; - else if (i < *decoded_options_count && j == fdecoded_options_count) - { - warning (0, "Extra option to %<-Xassembler%>: %s," - " dropping all %<-Xassembler%> and %<-Wa%> options.", - (*decoded_options)[i].arg); - xassembler_options_error = true; - break; - } - else if (i == *decoded_options_count && j < fdecoded_options_count) - { - warning (0, "Extra option to %<-Xassembler%>: %s," - " dropping all %<-Xassembler%> and %<-Wa%> options.", - fdecoded_options[j].arg); - xassembler_options_error = true; - break; - } - else if (strcmp ((*decoded_options)[i].arg, fdecoded_options[j].arg)) - { - warning (0, "Options to %<-Xassembler%> do not match: %s, %s," - " dropping all %<-Xassembler%> and %<-Wa%> options.", - (*decoded_options)[i].arg, fdecoded_options[j].arg); - xassembler_options_error = true; - break; - } + { + for (; i < *decoded_options_count; i++) + if ((*decoded_options)[i].opt_index == OPT_Xassembler) + break; + + for (; j < fdecoded_options_count; j++) + if (fdecoded_options[j].opt_index == OPT_Xassembler) + break; + + if (i == *decoded_options_count && j == fdecoded_options_count) + break; + else if (i < *decoded_options_count && j == fdecoded_options_count) { + warning (0, "Extra option to %<-Xassembler%>: %s," + " dropping all %<-Xassembler%> and %<-Wa%> options.", + (*decoded_options)[i].arg); + xassembler_options_error = true; + break; + } + else if (i == *decoded_options_count && j < fdecoded_options_count) { + warning (0, "Extra option to %<-Xassembler%>: %s," + " dropping all %<-Xassembler%> and %<-Wa%> options.", + fdecoded_options[j].arg); + xassembler_options_error = true; + break; + } + else if (strcmp ((*decoded_options)[i].arg, fdecoded_options[j].arg)) { + warning (0, "Options to %<-Xassembler%> do not match: %s, %s," + " dropping all %<-Xassembler%> and %<-Wa%> options.", + (*decoded_options)[i].arg, fdecoded_options[j].arg); + xassembler_options_error = true; + break; } + } } /* Auxiliary function that frees elements of PTR and PTR itself. N is number of elements to be freed. If PTR is NULL, nothing is freed. If an element is NULL, subsequent elements are not freed. */ -static void ** -free_array_of_ptrs (void **ptr, unsigned n) -{ +static void ** free_array_of_ptrs (void **ptr, unsigned n) { if (!ptr) return NULL; - for (unsigned i = 0; i < n; i++) - { - if (!ptr[i]) - break; - free (ptr[i]); - } + for (unsigned i = 0; i < n; i++) { + if (!ptr[i]) + break; + free (ptr[i]); + } free (ptr); return NULL; } @@ -556,19 +562,17 @@ free_array_of_ptrs (void **ptr, unsigned n) Tokens are assumed to be delimited by ':'. If APPEND is non-null, append it to every token we find. */ -static unsigned -parse_env_var (const char *str, char ***pvalues, const char *append) -{ +static unsigned parse_env_var (const char *str, char ***pvalues, const char *append) { + const char *curval, *nextval; char **values; unsigned num = 1, i; curval = strchr (str, ':'); - while (curval) - { - num++; - curval = strchr (curval + 1, ':'); - } + while (curval) { + num++; + curval = strchr (curval + 1, ':'); + } values = (char**) xmalloc (num * sizeof (char*)); curval = str; @@ -577,243 +581,220 @@ parse_env_var (const char *str, char ***pvalues, const char *append) nextval = strchr (curval, '\0'); int append_len = append ? strlen (append) : 0; - for (i = 0; i < num; i++) - { - int l = nextval - curval; - values[i] = (char*) xmalloc (l + 1 + append_len); - memcpy (values[i], curval, l); - values[i][l] = 0; - if (append) - strcat (values[i], append); - curval = nextval + 1; - nextval = strchr (curval, ':'); - if (nextval == NULL) - nextval = strchr (curval, '\0'); - } + for (i = 0; i < num; i++) { + int l = nextval - curval; + values[i] = (char*) xmalloc (l + 1 + append_len); + memcpy (values[i], curval, l); + values[i][l] = 0; + if (append) + strcat (values[i], append); + curval = nextval + 1; + nextval = strchr (curval, ':'); + if (nextval == NULL) + nextval = strchr (curval, '\0'); + } *pvalues = values; return num; } /* Append options OPTS from lto or offload_lto sections to ARGV_OBSTACK. */ -static void -append_compiler_options (obstack *argv_obstack, struct cl_decoded_option *opts, - unsigned int count) -{ +static void append_compiler_options (obstack *argv_obstack, + struct cl_decoded_option *opts, unsigned int count) { + /* Append compiler driver arguments as far as they were merged. */ - for (unsigned int j = 1; j < count; ++j) - { - struct cl_decoded_option *option = &opts[j]; - - /* File options have been properly filtered by lto-opts.c. */ - switch (option->opt_index) - { - /* Drop arguments that we want to take from the link line. */ - case OPT_flto_: - case OPT_flto: - case OPT_flto_partition_: - continue; - - default: - break; - } - - /* For now do what the original LTO option code was doing - pass - on any CL_TARGET flag and a few selected others. */ - switch (option->opt_index) - { - case OPT_fdiagnostics_show_caret: - case OPT_fdiagnostics_show_labels: - case OPT_fdiagnostics_show_line_numbers: - case OPT_fdiagnostics_show_option: - case OPT_fdiagnostics_show_location_: - case OPT_fshow_column: - case OPT_fPIC: - case OPT_fpic: - case OPT_fPIE: - case OPT_fpie: - case OPT_fcommon: - case OPT_fgnu_tm: - case OPT_fopenmp: - case OPT_fopenacc: - case OPT_fopenacc_dim_: - case OPT_foffload_abi_: - case OPT_g: - case OPT_O: - case OPT_Ofast: - case OPT_Og: - case OPT_Os: - break; - - case OPT_Xassembler: - /* When we detected a mismatch in assembler options between - the input TU's fall back to previous behavior of ignoring them. */ - if (xassembler_options_error) - continue; - break; - - default: - if (!(cl_options[option->opt_index].flags & CL_TARGET)) - continue; - } - - /* Pass the option on. */ - for (unsigned int i = 0; i < option->canonical_option_num_elements; ++i) - obstack_ptr_grow (argv_obstack, option->canonical_option[i]); + for (unsigned int j = 1; j < count; ++j) { + struct cl_decoded_option *option = &opts[j]; + + /* File options have been properly filtered by lto-opts.c. */ + switch (option->opt_index) { + /* Drop arguments that we want to take from the link line. */ + case OPT_flto_: + case OPT_flto: + case OPT_flto_partition_: + continue; + + default: + break; } + + /* For now do what the original LTO option code was doing - pass + on any CL_TARGET flag and a few selected others. */ + switch (option->opt_index) { + case OPT_fdiagnostics_show_caret: + case OPT_fdiagnostics_show_labels: + case OPT_fdiagnostics_show_line_numbers: + case OPT_fdiagnostics_show_option: + case OPT_fdiagnostics_show_location_: + case OPT_fshow_column: + case OPT_fPIC: + case OPT_fpic: + case OPT_fPIE: + case OPT_fpie: + case OPT_fcommon: + case OPT_fgnu_tm: + case OPT_fopenmp: + case OPT_fopenacc: + case OPT_fopenacc_dim_: + case OPT_foffload_abi_: + case OPT_g: + case OPT_O: + case OPT_Ofast: + case OPT_Og: + case OPT_Os: + break; + + case OPT_Xassembler: + /* When we detected a mismatch in assembler options between + the input TU's fall back to previous behavior of ignoring them. */ + if (xassembler_options_error) + continue; + break; + + default: + if (!(cl_options[option->opt_index].flags & CL_TARGET)) + continue; + } + + /* Pass the option on. */ + for (unsigned int i = 0; i < option->canonical_option_num_elements; ++i) + obstack_ptr_grow (argv_obstack, option->canonical_option[i]); + } } /* Append diag options in OPTS with length COUNT to ARGV_OBSTACK. */ -static void -append_diag_options (obstack *argv_obstack, struct cl_decoded_option *opts, - unsigned int count) -{ +static void append_diag_options (obstack *argv_obstack, + struct cl_decoded_option *opts, unsigned int count) { /* Append compiler driver arguments as far as they were merged. */ - for (unsigned int j = 1; j < count; ++j) - { - struct cl_decoded_option *option = &opts[j]; - - switch (option->opt_index) - { - case OPT_fdiagnostics_color_: - case OPT_fdiagnostics_format_: - case OPT_fdiagnostics_show_caret: - case OPT_fdiagnostics_show_labels: - case OPT_fdiagnostics_show_line_numbers: - case OPT_fdiagnostics_show_option: - case OPT_fdiagnostics_show_location_: - case OPT_fshow_column: - break; - default: - continue; - } - - /* Pass the option on. */ - for (unsigned int i = 0; i < option->canonical_option_num_elements; ++i) - obstack_ptr_grow (argv_obstack, option->canonical_option[i]); + for (unsigned int j = 1; j < count; ++j) { + struct cl_decoded_option *option = &opts[j]; + + switch (option->opt_index) { + case OPT_fdiagnostics_color_: + case OPT_fdiagnostics_format_: + case OPT_fdiagnostics_show_caret: + case OPT_fdiagnostics_show_labels: + case OPT_fdiagnostics_show_line_numbers: + case OPT_fdiagnostics_show_option: + case OPT_fdiagnostics_show_location_: + case OPT_fshow_column: + break; + default: + continue; } + + /* Pass the option on. */ + for (unsigned int i = 0; i < option->canonical_option_num_elements; ++i) + obstack_ptr_grow (argv_obstack, option->canonical_option[i]); + } } /* Append linker options OPTS to ARGV_OBSTACK. */ -static void -append_linker_options (obstack *argv_obstack, struct cl_decoded_option *opts, - unsigned int count) -{ +static void append_linker_options (obstack *argv_obstack, + struct cl_decoded_option *opts, unsigned int count) { /* Append linker driver arguments. Compiler options from the linker driver arguments will override / merge with those from the compiler. */ - for (unsigned int j = 1; j < count; ++j) - { - struct cl_decoded_option *option = &opts[j]; - - /* Do not pass on frontend specific flags not suitable for lto. */ - if (!(cl_options[option->opt_index].flags - & (CL_COMMON|CL_TARGET|CL_DRIVER|CL_LTO))) - continue; - - switch (option->opt_index) - { - case OPT_o: - case OPT_flto_: - case OPT_flto: - /* We've handled these LTO options, do not pass them on. */ - continue; - - case OPT_fopenmp: - case OPT_fopenacc: - /* Ignore -fno-XXX form of these options, as otherwise - corresponding builtins will not be enabled. */ - if (option->value == 0) - continue; - break; - - default: - break; - } - - /* Pass the option on. */ - for (unsigned int i = 0; i < option->canonical_option_num_elements; ++i) - obstack_ptr_grow (argv_obstack, option->canonical_option[i]); + for (unsigned int j = 1; j < count; ++j) { + struct cl_decoded_option *option = &opts[j]; + + /* Do not pass on frontend specific flags not suitable for lto. */ + if (!(cl_options[option->opt_index].flags + & (CL_COMMON|CL_TARGET|CL_DRIVER|CL_LTO))) + continue; + + switch (option->opt_index) { + case OPT_o: + case OPT_flto_: + case OPT_flto: + /* We've handled these LTO options, do not pass them on. */ + continue; + + case OPT_fopenmp: + case OPT_fopenacc: + /* Ignore -fno-XXX form of these options, as otherwise + corresponding builtins will not be enabled. */ + if (option->value == 0) + continue; + break; + + default: + break; } + + /* Pass the option on. */ + for (unsigned int i = 0; i < option->canonical_option_num_elements; ++i) + obstack_ptr_grow (argv_obstack, option->canonical_option[i]); + } } /* Extract options for TARGET offload compiler from OPTIONS and append them to ARGV_OBSTACK. */ -static void -append_offload_options (obstack *argv_obstack, const char *target, - struct cl_decoded_option *options, - unsigned int options_count) -{ - for (unsigned i = 0; i < options_count; i++) - { - const char *cur, *next, *opts; - char **argv; - unsigned argc; - struct cl_decoded_option *option = &options[i]; - - if (option->opt_index != OPT_foffload_) - continue; - - /* If option argument starts with '-' then no target is specified. That - means offload options are specified for all targets, so we need to - append them. */ - if (option->arg[0] == '-') - opts = option->arg; - else - { - opts = strchr (option->arg, '='); - /* If there are offload targets specified, but no actual options, - there is nothing to do here. */ - if (!opts) - continue; - - cur = option->arg; - - while (cur < opts) - { - next = strchr (cur, ','); - if (next == NULL) - next = opts; - next = (next > opts) ? opts : next; - - /* Are we looking for this offload target? */ - if (strlen (target) == (size_t) (next - cur) - && strncmp (target, cur, next - cur) == 0) - break; - - /* Skip the comma or equal sign. */ - cur = next + 1; - } - - if (cur >= opts) - continue; - - opts++; - } - - argv = buildargv (opts); - for (argc = 0; argv[argc]; argc++) - obstack_ptr_grow (argv_obstack, argv[argc]); +static void append_offload_options (obstack *argv_obstack, const char *target, + struct cl_decoded_option *options, unsigned int options_count) { + + for (unsigned i = 0; i < options_count; i++) { + const char *cur, *next, *opts; + char **argv; + unsigned argc; + struct cl_decoded_option *option = &options[i]; + + if (option->opt_index != OPT_foffload_) + continue; + + /* If option argument starts with '-' then no target is specified. That + means offload options are specified for all targets, so we need to + append them. */ + if (option->arg[0] == '-') + opts = option->arg; + else { + opts = strchr (option->arg, '='); + /* If there are offload targets specified, but no actual options, + there is nothing to do here. */ + if (!opts) + continue; + + cur = option->arg; + + while (cur < opts) { + next = strchr (cur, ','); + if (next == NULL) + next = opts; + next = (next > opts) ? opts : next; + + /* Are we looking for this offload target? */ + if (strlen (target) == (size_t) (next - cur) && strncmp (target, cur, next - cur) == 0) + break; + + /* Skip the comma or equal sign. */ + cur = next + 1; + } + + if (cur >= opts) + continue; + + opts++; } + + argv = buildargv (opts); + for (argc = 0; argv[argc]; argc++) + obstack_ptr_grow (argv_obstack, argv[argc]); + } } /* Check whether NAME can be accessed in MODE. This is like access, except that it never considers directories to be executable. */ -static int -access_check (const char *name, int mode) -{ - if (mode == X_OK) - { - struct stat st; +static int access_check (const char *name, int mode) { + if (mode == X_OK) { + struct stat st; - if (stat (name, &st) < 0 - || S_ISDIR (st.st_mode)) - return -1; - } + if (stat (name, &st) < 0 || S_ISDIR (st.st_mode)) + return -1; + } return access (name, mode); } @@ -821,19 +802,16 @@ access_check (const char *name, int mode) /* Prepare a target image for offload TARGET, using mkoffload tool from COMPILER_PATH. Return the name of the resultant object file. */ -static char * -compile_offload_image (const char *target, const char *compiler_path, - unsigned in_argc, char *in_argv[], - struct cl_decoded_option *compiler_opts, - unsigned int compiler_opt_count, - struct cl_decoded_option *linker_opts, - unsigned int linker_opt_count) -{ +static char * compile_offload_image (const char *target, const char *compiler_path, + unsigned in_argc, char *in_argv[], + struct cl_decoded_option *compiler_opts, + unsigned int compiler_opt_count, + struct cl_decoded_option *linker_opts, + unsigned int linker_opt_count) { + char *filename = NULL; - char *dumpbase; char **argv; - char *suffix - = XALLOCAVEC (char, sizeof ("/accel//mkoffload") + strlen (target)); + char *suffix = XALLOCAVEC (char, sizeof ("/accel//mkoffload") + strlen (target)); strcpy (suffix, "/accel/"); strcat (suffix, target); strcat (suffix, "/mkoffload"); @@ -843,24 +821,18 @@ compile_offload_image (const char *target, const char *compiler_path, const char *compiler = NULL; for (unsigned i = 0; i < n_paths; i++) - if (access_check (paths[i], X_OK) == 0) - { - compiler = paths[i]; - break; - } + if (access_check (paths[i], X_OK) == 0) { + compiler = paths[i]; + break; + } if (!compiler) fatal_error (input_location, - "could not find %s in %s (consider using %<-B%>)", - suffix + 1, compiler_path); - - dumpbase = concat (dumppfx, "x", target, NULL); + "could not find %s in %s (consider using %<-B%>)", + suffix + 1, compiler_path); /* Generate temporary output file name. */ - if (save_temps) - filename = concat (dumpbase, ".o", NULL); - else - filename = make_temp_file (".target.o"); + filename = make_temp_file (".target.o"); struct obstack argv_obstack; obstack_init (&argv_obstack); @@ -878,18 +850,15 @@ compile_offload_image (const char *target, const char *compiler_path, /* Append options from offload_lto sections. */ append_compiler_options (&argv_obstack, compiler_opts, - compiler_opt_count); + compiler_opt_count); append_diag_options (&argv_obstack, linker_opts, linker_opt_count); - obstack_ptr_grow (&argv_obstack, "-dumpbase"); - obstack_ptr_grow (&argv_obstack, dumpbase); - /* Append options specified by -foffload last. In case of conflicting options we expect offload compiler to choose the latest. */ append_offload_options (&argv_obstack, target, compiler_opts, - compiler_opt_count); + compiler_opt_count); append_offload_options (&argv_obstack, target, linker_opts, - linker_opt_count); + linker_opt_count); obstack_ptr_grow (&argv_obstack, NULL); argv = XOBFINISH (&argv_obstack, char **); @@ -906,13 +875,11 @@ compile_offload_image (const char *target, const char *compiler_path, IN_ARGV specify options and input object files. As all of them could contain target sections, we pass them all to target compilers. */ -static void -compile_images_for_offload_targets (unsigned in_argc, char *in_argv[], - struct cl_decoded_option *compiler_opts, - unsigned int compiler_opt_count, - struct cl_decoded_option *linker_opts, - unsigned int linker_opt_count) -{ +static void compile_images_for_offload_targets (unsigned in_argc, + char *in_argv[], struct cl_decoded_option *compiler_opts, + unsigned int compiler_opt_count, struct cl_decoded_option *linker_opts, + unsigned int linker_opt_count) { + char **names = NULL; const char *target_names = getenv (OFFLOAD_TARGET_NAMES_ENV); if (!target_names) @@ -927,57 +894,52 @@ compile_images_for_offload_targets (unsigned in_argc, char *in_argv[], /* Prepare an image for each target and save the name of the resultant object file to the OFFLOAD_NAMES array. It is terminated by a NULL entry. */ offload_names = XCNEWVEC (char *, num_targets + 1); - for (unsigned i = 0; i < num_targets; i++) - { - /* HSA does not use LTO-like streaming and a different compiler, skip - it. */ - if (strcmp (names[i], "hsa") == 0) - continue; - - offload_names[next_name_entry] - = compile_offload_image (names[i], compiler_path, in_argc, in_argv, - compiler_opts, compiler_opt_count, - linker_opts, linker_opt_count); - if (!offload_names[next_name_entry]) - fatal_error (input_location, - "problem with building target image for %s", names[i]); - next_name_entry++; - } - - out: + for (unsigned i = 0; i < num_targets; i++) { + /* HSA does not use LTO-like streaming and a different compiler, skip + it. */ + if (strcmp (names[i], "hsa") == 0) + continue; + + offload_names[next_name_entry] + = compile_offload_image (names[i], compiler_path, in_argc, in_argv, + compiler_opts, compiler_opt_count, + linker_opts, linker_opt_count); + if (!offload_names[next_name_entry]) + fatal_error (input_location, + "problem with building target image for %s", names[i]); + next_name_entry++; + } + +out: free_array_of_ptrs ((void **) names, num_targets); } /* Copy a file from SRC to DEST. */ - -static void -copy_file (const char *dest, const char *src) +static void copy_file (const char *dest, const char *src) { - FILE *d = fopen (dest, "wb"); - FILE *s = fopen (src, "rb"); - char buffer[512]; - while (!feof (s)) - { - size_t len = fread (buffer, 1, 512, s); - if (ferror (s) != 0) - fatal_error (input_location, "reading input file"); - if (len > 0) - { - fwrite (buffer, 1, len, d); - if (ferror (d) != 0) - fatal_error (input_location, "writing output file"); - } - } - fclose (d); - fclose (s); +FILE *d = fopen (dest, "wb"); +FILE *s = fopen (src, "rb"); +char buffer[512]; +while (!feof (s)) +{ +size_t len = fread (buffer, 1, 512, s); +if (ferror (s) != 0) + fatal_error (input_location, "reading input file"); +if (len > 0) +{ +fwrite (buffer, 1, len, d); +if (ferror (d) != 0) + fatal_error (input_location, "writing output file"); +} +} +fclose (d); +fclose (s); } /* Find the crtoffloadtable.o file in LIBRARY_PATH, make copy and pass name of the copy to the linker. */ -static void -find_crtoffloadtable (void) -{ +static void find_crtoffloadtable (void) { char **paths = NULL; const char *library_path = getenv ("LIBRARY_PATH"); if (!library_path) @@ -986,18 +948,17 @@ find_crtoffloadtable (void) unsigned i; for (i = 0; i < n_paths; i++) - if (access_check (paths[i], R_OK) == 0) - { - /* The linker will delete the filename we give it, so make a copy. */ - char *crtoffloadtable = make_temp_file (".crtoffloadtable.o"); - copy_file (crtoffloadtable, paths[i]); - printf ("%s\n", crtoffloadtable); - XDELETEVEC (crtoffloadtable); - break; - } + if (access_check (paths[i], R_OK) == 0) { + /* The linker will delete the filename we give it, so make a copy. */ + char *crtoffloadtable = make_temp_file (".crtoffloadtable.o"); + copy_file (crtoffloadtable, paths[i]); + printf ("%s\n", crtoffloadtable); + XDELETEVEC (crtoffloadtable); + break; + } if (i == n_paths) fatal_error (input_location, - "installation error, cannot find %"); + "installation error, cannot find %"); free_array_of_ptrs ((void **) paths, n_paths); } @@ -1008,11 +969,10 @@ find_crtoffloadtable (void) otherwise. COLLECT_GCC holds the value of the environment variable with the same name. */ -static bool -find_and_merge_options (int fd, off_t file_offset, const char *prefix, - struct cl_decoded_option **opts, - unsigned int *opt_count, const char *collect_gcc) -{ +static bool find_and_merge_options (int fd, off_t file_offset, + const char *prefix, struct cl_decoded_option **opts, unsigned int *opt_count, + const char *collect_gcc) { + off_t offset, length; char *data; char *fopts; @@ -1023,7 +983,7 @@ find_and_merge_options (int fd, off_t file_offset, const char *prefix, simple_object_read *sobj; sobj = simple_object_start_read (fd, file_offset, "__GNU_LTO", - &errmsg, &err); + &errmsg, &err); if (!sobj) return false; @@ -1031,35 +991,32 @@ find_and_merge_options (int fd, off_t file_offset, const char *prefix, strcpy (secname, prefix); strcat (secname, ".opts"); if (!simple_object_find_section (sobj, secname, &offset, &length, - &errmsg, &err)) - { - simple_object_release_read (sobj); - return false; - } + &errmsg, &err)) { + simple_object_release_read (sobj); + return false; + } lseek (fd, file_offset + offset, SEEK_SET); data = (char *)xmalloc (length); read (fd, data, length); fopts = data; - do - { - struct cl_decoded_option *f2decoded_options; - unsigned int f2decoded_options_count; - get_options_from_collect_gcc_options (collect_gcc, fopts, - &f2decoded_options, - &f2decoded_options_count); - if (!fdecoded_options) - { - fdecoded_options = f2decoded_options; - fdecoded_options_count = f2decoded_options_count; - } - else - merge_and_complain (&fdecoded_options, - &fdecoded_options_count, - f2decoded_options, f2decoded_options_count); - - fopts += strlen (fopts) + 1; + do { + struct cl_decoded_option *f2decoded_options; + unsigned int f2decoded_options_count; + get_options_from_collect_gcc_options (collect_gcc, fopts, + &f2decoded_options, + &f2decoded_options_count); + if (!fdecoded_options) { + fdecoded_options = f2decoded_options; + fdecoded_options_count = f2decoded_options_count; } + else + merge_and_complain (&fdecoded_options, + &fdecoded_options_count, + f2decoded_options, f2decoded_options_count); + + fopts += strlen (fopts) + 1; + } while (fopts - data < length); free (data); @@ -1072,9 +1029,7 @@ find_and_merge_options (int fd, off_t file_offset, const char *prefix, /* Copy early debug info sections from INFILE to a new file whose name is returned. Return NULL on error. */ -const char * -debug_objcopy (const char *infile, bool rename) -{ +const char * debug_objcopy (const char *infile, bool rename) { char *outfile; const char *errmsg; int err; @@ -1085,46 +1040,43 @@ debug_objcopy (const char *infile, bool rename) long loffset; int consumed; if ((p = strrchr (infile, '@')) - && p != infile - && sscanf (p, "@%li%n", &loffset, &consumed) >= 1 - && strlen (p) == (unsigned int) consumed) - { - char *fname = xstrdup (infile); - fname[p - infile] = '\0'; - infile = fname; - inoff = (off_t) loffset; - } + && p != infile + && sscanf (p, "@%li%n", &loffset, &consumed) >= 1 + && strlen (p) == (unsigned int) consumed) { + char *fname = xstrdup (infile); + fname[p - infile] = '\0'; + infile = fname; + inoff = (off_t) loffset; + } int infd = open (infile, O_RDONLY | O_BINARY); if (infd == -1) return NULL; simple_object_read *inobj = simple_object_start_read (infd, inoff, - "__GNU_LTO", - &errmsg, &err); + "__GNU_LTO", + &errmsg, &err); if (!inobj) return NULL; off_t off, len; if (simple_object_find_section (inobj, ".gnu.debuglto_.debug_info", - &off, &len, &errmsg, &err) != 1) - { - if (errmsg) - fatal_error (0, "%s: %s", errmsg, xstrerror (err)); + &off, &len, &errmsg, &err) != 1) { + if (errmsg) + fatal_error (0, "%s: %s", errmsg, xstrerror (err)); - simple_object_release_read (inobj); - close (infd); - return NULL; - } + simple_object_release_read (inobj); + close (infd); + return NULL; + } if (save_temps) outfile = concat (orig_infile, ".debug.temp.o", NULL); else outfile = make_temp_file (".debug.temp.o"); errmsg = simple_object_copy_lto_debug_sections (inobj, outfile, &err, rename); - if (errmsg) - { - unlink_if_ordinary (outfile); - fatal_error (0, "%s: %s", errmsg, xstrerror (err)); - } + if (errmsg) { + unlink_if_ordinary (outfile); + fatal_error (0, "%s: %s", errmsg, xstrerror (err)); + } simple_object_release_read (inobj); close (infd); @@ -1134,9 +1086,7 @@ debug_objcopy (const char *infile, bool rename) /* Helper for qsort: compare priorities for parallel compilation. */ -int -cmp_priority (const void *a, const void *b) -{ +int cmp_priority (const void *a, const void *b) { return *((const int *)b)-*((const int *)a); } @@ -1150,8 +1100,7 @@ static unsigned long get_cpuset_size; cpu_set_t *cpusetp; unsigned long -static cpuset_popcount (unsigned long cpusetsize, cpu_set_t *cpusetp) -{ +static cpuset_popcount (unsigned long cpusetsize, cpu_set_t *cpusetp) { #ifdef CPU_COUNT_S /* glibc 2.7 and above provide a macro for this. */ return CPU_COUNT_S (cpusetsize, cpusetp); @@ -1164,13 +1113,12 @@ static cpuset_popcount (unsigned long cpusetsize, cpu_set_t *cpusetp) size_t i; unsigned long ret = 0; STATIC_ASSERT (sizeof (cpusetp->__bits[0]) == sizeof (unsigned long int)); - for (i = 0; i < cpusetsize / sizeof (cpusetp->__bits[0]); i++) - { - unsigned long int mask = cpusetp->__bits[i]; - if (mask == 0) - continue; - ret += __builtin_popcountl (mask); - } + for (i = 0; i < cpusetsize / sizeof (cpusetp->__bits[0]); i++) { + unsigned long int mask = cpusetp->__bits[i]; + if (mask == 0) + continue; + ret += __builtin_popcountl (mask); + } return ret; #endif } @@ -1179,9 +1127,7 @@ static cpuset_popcount (unsigned long cpusetsize, cpu_set_t *cpusetp) /* At startup, determine the default number of threads. It would seem this should be related to the number of cpus online. */ -static void -init_num_threads (void) -{ +static void init_num_threads (void) { #ifdef HAVE_PTHREAD_AFFINITY_NP #if defined (_SC_NPROCESSORS_CONF) && defined (CPU_ALLOC_SIZE) cpuset_size = sysconf (_SC_NPROCESSORS_CONF); @@ -1191,49 +1137,47 @@ init_num_threads (void) #endif cpusetp = (cpu_set_t *) xmalloc (gomp_cpuset_size); - do - { - int ret = pthread_getaffinity_np (pthread_self (), gomp_cpuset_size, - cpusetp); - if (ret == 0) - { - /* Count only the CPUs this process can use. */ - nthreads_var = cpuset_popcount (cpuset_size, cpusetp); - if (nthreads_var == 0) - break; - get_cpuset_size = cpuset_size; + do { + int ret = pthread_getaffinity_np (pthread_self (), gomp_cpuset_size, + cpusetp); + if (ret == 0) { + /* Count only the CPUs this process can use. */ + nthreads_var = cpuset_popcount (cpuset_size, cpusetp); + if (nthreads_var == 0) + break; + get_cpuset_size = cpuset_size; #ifdef CPU_ALLOC_SIZE - unsigned long i; - for (i = cpuset_size * 8; i; i--) - if (CPU_ISSET_S (i - 1, cpuset_size, cpusetp)) - break; - cpuset_size = CPU_ALLOC_SIZE (i); + unsigned long i; + for (i = cpuset_size * 8; i; i--) + if (CPU_ISSET_S (i - 1, cpuset_size, cpusetp)) + break; + cpuset_size = CPU_ALLOC_SIZE (i); #endif - return; - } - if (ret != EINVAL) - break; + return; + } + if (ret != EINVAL) + break; #ifdef CPU_ALLOC_SIZE - if (cpuset_size < sizeof (cpu_set_t)) - cpuset_size = sizeof (cpu_set_t); - else - cpuset_size = cpuset_size * 2; - if (cpuset_size < 8 * sizeof (cpu_set_t)) - cpusetp - = (cpu_set_t *) realloc (cpusetp, cpuset_size); - else - { - /* Avoid fatal if too large memory allocation would be - requested, e.g. kernel returning EINVAL all the time. */ - void *p = realloc (cpusetp, cpuset_size); - if (p == NULL) - break; - cpusetp = (cpu_set_t *) p; - } + if (cpuset_size < sizeof (cpu_set_t)) + cpuset_size = sizeof (cpu_set_t); + else + cpuset_size = cpuset_size * 2; + if (cpuset_size < 8 * sizeof (cpu_set_t)) + cpusetp + = (cpu_set_t *) realloc (cpusetp, cpuset_size); + else + { + /* Avoid fatal if too large memory allocation would be + requested, e.g. kernel returning EINVAL all the time. */ + void *p = realloc (cpusetp, cpuset_size); + if (p == NULL) + break; + cpusetp = (cpu_set_t *) p; + } #else - break; + break; #endif - } + } while (1); cpuset_size = 0; nthreads_var = 1; @@ -1249,11 +1193,9 @@ init_num_threads (void) /* Test and return reason why a jobserver cannot be detected. */ -static const char * -jobserver_active_p (void) -{ - #define JS_PREFIX "jobserver is not available: " - #define JS_NEEDLE "--jobserver-auth=" +static const char * jobserver_active_p (void) { +#define JS_PREFIX "jobserver is not available: " +#define JS_NEEDLE "--jobserver-auth=" const char *makeflags = getenv ("MAKEFLAGS"); if (makeflags == NULL) @@ -1267,10 +1209,10 @@ jobserver_active_p (void) int wfd = -1; if (sscanf (n + strlen (JS_NEEDLE), "%d,%d", &rfd, &wfd) == 2 - && rfd > 0 - && wfd > 0 - && is_valid_fd (rfd) - && is_valid_fd (wfd)) + && rfd > 0 + && wfd > 0 + && is_valid_fd (rfd) + && is_valid_fd (wfd)) return NULL; else return JS_PREFIX "cannot access %<" JS_NEEDLE "%> file descriptors"; @@ -1278,9 +1220,9 @@ jobserver_active_p (void) /* Execute gcc. ARGC is the number of arguments. ARGV contains the arguments. */ -static void -run_gcc (unsigned argc, char *argv[]) -{ +static void run_gcc (unsigned argc, char *argv[]) { + fprintf(stderr, "JOHN PRINT: run_gcc\n"); + unsigned i, j; const char **new_argv; const char **argv_ptr; @@ -1307,41 +1249,40 @@ run_gcc (unsigned argc, char *argv[]) bool linker_output_rel = false; bool skip_debug = false; unsigned n_debugobj; - const char *incoming_dumppfx = dumppfx = NULL; + const char *dumppfx = NULL, *incoming_dumppfx = NULL; static char current_dir[] = { '.', DIR_SEPARATOR, '\0' }; /* Get the driver and options. */ collect_gcc = getenv ("COLLECT_GCC"); if (!collect_gcc) fatal_error (input_location, - "environment variable % must be set"); + "environment variable % must be set"); collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS"); if (!collect_gcc_options) fatal_error (input_location, - "environment variable % must be set"); + "environment variable % must be set"); char *collect_as_options = getenv ("COLLECT_AS_OPTIONS"); /* Prepend -Xassembler to each option, and append the string to collect_gcc_options. */ - if (collect_as_options) - { - obstack temporary_obstack; - obstack_init (&temporary_obstack); + if (collect_as_options) { + obstack temporary_obstack; + obstack_init (&temporary_obstack); - prepend_xassembler_to_collect_as_options (collect_as_options, - &temporary_obstack); - obstack_1grow (&temporary_obstack, '\0'); + prepend_xassembler_to_collect_as_options (collect_as_options, + &temporary_obstack); + obstack_1grow (&temporary_obstack, '\0'); - char *xassembler_opts_string - = XOBFINISH (&temporary_obstack, char *); - collect_gcc_options = concat (collect_gcc_options, xassembler_opts_string, - NULL); - } + char *xassembler_opts_string + = XOBFINISH (&temporary_obstack, char *); + collect_gcc_options = concat (collect_gcc_options, xassembler_opts_string, + NULL); + } get_options_from_collect_gcc_options (collect_gcc, collect_gcc_options, - &decoded_options, - &decoded_options_count); + &decoded_options, + &decoded_options_count); /* Allocate array for input object files with LTO IL, and for possible preceding arguments. */ @@ -1349,56 +1290,53 @@ run_gcc (unsigned argc, char *argv[]) ltoobj_argv = XNEWVEC (char *, argc); /* Look at saved options in the IL files. */ - for (i = 1; i < argc; ++i) - { - char *p; - int fd; - off_t file_offset = 0; - long loffset; - int consumed; - char *filename = argv[i]; - - if (strncmp (argv[i], "-foffload-objects=", - sizeof ("-foffload-objects=") - 1) == 0) - { - have_offload = true; - offload_objects_file_name - = argv[i] + sizeof ("-foffload-objects=") - 1; - continue; - } - - if ((p = strrchr (argv[i], '@')) - && p != argv[i] - && sscanf (p, "@%li%n", &loffset, &consumed) >= 1 - && strlen (p) == (unsigned int) consumed) - { - filename = XNEWVEC (char, p - argv[i] + 1); - memcpy (filename, argv[i], p - argv[i]); - filename[p - argv[i]] = '\0'; - file_offset = (off_t) loffset; - } - fd = open (filename, O_RDONLY | O_BINARY); - /* Linker plugin passes -fresolution and -flinker-output options. - -flinker-output is passed only when user did not specify one and thus - we do not need to worry about duplicities with the option handling - below. */ - if (fd == -1) - { - lto_argv[lto_argc++] = argv[i]; - if (strcmp (argv[i], "-flinker-output=rel") == 0) - linker_output_rel = true; - continue; - } - - if (find_and_merge_options (fd, file_offset, LTO_SECTION_NAME_PREFIX, - &fdecoded_options, &fdecoded_options_count, - collect_gcc)) - { - have_lto = true; - ltoobj_argv[ltoobj_argc++] = argv[i]; - } - close (fd); + for (i = 1; i < argc; ++i) { + char *p; + int fd; + off_t file_offset = 0; + long loffset; + int consumed; + char *filename = argv[i]; + + if (strncmp (argv[i], "-foffload-objects=", + sizeof ("-foffload-objects=") - 1) == 0) { + + have_offload = true; + offload_objects_file_name + = argv[i] + sizeof ("-foffload-objects=") - 1; + continue; + } + + if ((p = strrchr (argv[i], '@')) + && p != argv[i] + && sscanf (p, "@%li%n", &loffset, &consumed) >= 1 + && strlen (p) == (unsigned int) consumed) { + + filename = XNEWVEC (char, p - argv[i] + 1); + memcpy (filename, argv[i], p - argv[i]); + filename[p - argv[i]] = '\0'; + file_offset = (off_t) loffset; + } + fd = open (filename, O_RDONLY | O_BINARY); + /* Linker plugin passes -fresolution and -flinker-output options. + -flinker-output is passed only when user did not specify one and thus + we do not need to worry about duplicities with the option handling + below. */ + if (fd == -1) { + lto_argv[lto_argc++] = argv[i]; + if (strcmp (argv[i], "-flinker-output=rel") == 0) + linker_output_rel = true; + continue; + } + + if (find_and_merge_options (fd, file_offset, LTO_SECTION_NAME_PREFIX, + &fdecoded_options, &fdecoded_options_count, + collect_gcc)) { + have_lto = true; + ltoobj_argv[ltoobj_argc++] = argv[i]; } + close (fd); + } /* Initalize the common arguments for the driver. */ obstack_init (&argv_obstack); @@ -1407,134 +1345,122 @@ run_gcc (unsigned argc, char *argv[]) obstack_ptr_grow (&argv_obstack, "-c"); append_compiler_options (&argv_obstack, fdecoded_options, - fdecoded_options_count); + fdecoded_options_count); append_linker_options (&argv_obstack, decoded_options, decoded_options_count); /* Scan linker driver arguments for things that are of relevance to us. */ - for (j = 1; j < decoded_options_count; ++j) - { - struct cl_decoded_option *option = &decoded_options[j]; - switch (option->opt_index) - { - case OPT_o: - linker_output = option->arg; - break; - - /* We don't have to distinguish between -save-temps=* and - -save-temps, -dumpdir already carries that - information. */ - case OPT_save_temps_: - case OPT_save_temps: - save_temps = 1; - break; - - case OPT_v: - verbose = 1; - break; - - case OPT_flto_partition_: - if (strcmp (option->arg, "none") == 0) - no_partition = true; - break; - - case OPT_flto_: - if (strcmp (option->arg, "jobserver") == 0) - { - parallel = 1; - jobserver = 1; - } - else if (strcmp (option->arg, "auto") == 0) - { - parallel = 1; - auto_parallel = 1; - } - else - { - parallel = atoi (option->arg); - if (parallel <= 1) - parallel = 0; - } - /* Fallthru. */ - - case OPT_flto: - lto_mode = LTO_MODE_WHOPR; - break; - - case OPT_flinker_output_: - linker_output_rel = !strcmp (option->arg, "rel"); - break; - - case OPT_g: - /* Recognize -g0. */ - skip_debug = option->arg && !strcmp (option->arg, "0"); - break; - - case OPT_dumpdir: - incoming_dumppfx = dumppfx = option->arg; - break; - - default: - break; - } + for (j = 1; j < decoded_options_count; ++j) { + struct cl_decoded_option *option = &decoded_options[j]; + switch (option->opt_index) { + case OPT_o: + linker_output = option->arg; + break; + + /* We don't have to distinguish between -save-temps=* and + -save-temps, -dumpdir already carries that + information. */ + case OPT_save_temps_: + case OPT_save_temps: + save_temps = 1; + break; + + case OPT_v: + verbose = 1; + break; + + case OPT_flto_partition_: + if (strcmp (option->arg, "none") == 0) + no_partition = true; + break; + + case OPT_flto_: + if (strcmp (option->arg, "jobserver") == 0) { + parallel = 1; + jobserver = 1; + } + else if (strcmp (option->arg, "auto") == 0) { + parallel = 1; + auto_parallel = 1; + } + else { + parallel = atoi (option->arg); + if (parallel <= 1) + parallel = 0; + } + /* Fallthru. */ + + case OPT_flto: + lto_mode = LTO_MODE_WHOPR; + break; + + case OPT_flinker_output_: + linker_output_rel = !strcmp (option->arg, "rel"); + break; + + case OPT_g: + /* Recognize -g0. */ + skip_debug = option->arg && !strcmp (option->arg, "0"); + break; + + case OPT_dumpdir: + incoming_dumppfx = dumppfx = option->arg; + break; + + default: + break; } + } /* Output lto-wrapper invocation command. */ - if (verbose) - { - for (i = 0; i < argc; ++i) - { - fputs (argv[i], stderr); - fputc (' ', stderr); - } - fputc ('\n', stderr); + if (verbose) { + for (i = 0; i < argc; ++i) { + fputs (argv[i], stderr); + fputc (' ', stderr); } + fputc ('\n', stderr); + } if (linker_output_rel) no_partition = true; - if (no_partition) - { - lto_mode = LTO_MODE_LTO; - jobserver = 0; - auto_parallel = 0; - parallel = 0; + if (no_partition) { + lto_mode = LTO_MODE_LTO; + jobserver = 0; + auto_parallel = 0; + parallel = 0; + } + else { + const char *jobserver_error = jobserver_active_p (); + if (jobserver && jobserver_error != NULL) + warning (0, jobserver_error); + else if (!jobserver && jobserver_error == NULL) { + parallel = 1; + jobserver = 1; } - else - { - const char *jobserver_error = jobserver_active_p (); - if (jobserver && jobserver_error != NULL) - warning (0, jobserver_error); - else if (!jobserver && jobserver_error == NULL) - { - parallel = 1; - jobserver = 1; - } - } - - if (!dumppfx) - { - if (!linker_output - || strcmp (linker_output, HOST_BIT_BUCKET) == 0) - dumppfx = "a."; - else - { - const char *obase = lbasename (linker_output), *temp; - - /* Strip the executable extension. */ - size_t blen = strlen (obase), xlen; - if ((temp = strrchr (obase + 1, '.')) - && (xlen = strlen (temp)) - && (strcmp (temp, ".exe") == 0 + } + + if (!dumppfx) { + if (!linker_output + || strcmp (linker_output, HOST_BIT_BUCKET) == 0) + dumppfx = "a."; + else { + const char *obase = lbasename (linker_output), *temp; + + /* Strip the executable extension. */ + size_t blen = strlen (obase), xlen; + if ((temp = strrchr (obase + 1, '.')) + && (xlen = strlen (temp)) + && (strcmp (temp, ".exe") == 0 #if defined(HAVE_TARGET_EXECUTABLE_SUFFIX) - || strcmp (temp, TARGET_EXECUTABLE_SUFFIX) == 0 + || strcmp (temp, TARGET_EXECUTABLE_SUFFIX) == 0 #endif - || strcmp (obase, "a.out") == 0)) - dumppfx = xstrndup (linker_output, - obase - linker_output + blen - xlen + 1); - else - dumppfx = concat (linker_output, ".", NULL); - } + || strcmp (obase, "a.out") == 0)) + dumppfx = xstrndup (linker_output, + obase - linker_output + blen - xlen + 1); + else + dumppfx = concat (linker_output, ".", NULL); } + } /* If there's no directory component in the dumppfx, add one, so that, when it is used as -dumpbase, it overrides any occurrence @@ -1546,166 +1472,155 @@ run_gcc (unsigned argc, char *argv[]) -dumpbase overriding semantics. If we got an incoming -dumpdir argument, we'll pass it on, so don't bother with another one then. */ - if (!incoming_dumppfx) - { - obstack_ptr_grow (&argv_obstack, "-dumpdir"); - obstack_ptr_grow (&argv_obstack, ""); - } + if (!incoming_dumppfx) { + obstack_ptr_grow (&argv_obstack, "-dumpdir"); + obstack_ptr_grow (&argv_obstack, ""); + } obstack_ptr_grow (&argv_obstack, "-dumpbase"); /* Remember at which point we can scrub args to re-use the commons. */ new_head_argc = obstack_object_size (&argv_obstack) / sizeof (void *); - if (have_offload) - { - unsigned i, num_offload_files; - char **offload_argv; - FILE *f; - - f = fopen (offload_objects_file_name, "r"); - if (f == NULL) - fatal_error (input_location, "cannot open %s: %m", - offload_objects_file_name); - if (fscanf (f, "%u ", &num_offload_files) != 1) - fatal_error (input_location, "cannot read %s: %m", - offload_objects_file_name); - offload_argv = XCNEWVEC (char *, num_offload_files); - - /* Read names of object files with offload. */ - for (i = 0; i < num_offload_files; i++) - { - const unsigned piece = 32; - char *buf, *filename = XNEWVEC (char, piece); - size_t len; - - buf = filename; + if (have_offload) { + unsigned i, num_offload_files; + char **offload_argv; + FILE *f; + + f = fopen (offload_objects_file_name, "r"); + if (f == NULL) + fatal_error (input_location, "cannot open %s: %m", + offload_objects_file_name); + if (fscanf (f, "%u ", &num_offload_files) != 1) + fatal_error (input_location, "cannot read %s: %m", + offload_objects_file_name); + offload_argv = XCNEWVEC (char *, num_offload_files); + + /* Read names of object files with offload. */ + for (i = 0; i < num_offload_files; i++) { + const unsigned piece = 32; + char *buf, *filename = XNEWVEC (char, piece); + size_t len; + + buf = filename; cont1: - if (!fgets (buf, piece, f)) - break; - len = strlen (filename); - if (filename[len - 1] != '\n') - { - filename = XRESIZEVEC (char, filename, len + piece); - buf = filename + len; - goto cont1; - } - filename[len - 1] = '\0'; - offload_argv[i] = filename; - } - fclose (f); - if (offload_argv[num_offload_files - 1] == NULL) - fatal_error (input_location, "invalid format of %s", - offload_objects_file_name); - maybe_unlink (offload_objects_file_name); - offload_objects_file_name = NULL; - - /* Look at saved offload options in files. */ - for (i = 0; i < num_offload_files; i++) - { - char *p; - long loffset; - int fd, consumed; - off_t file_offset = 0; - char *filename = offload_argv[i]; - - if ((p = strrchr (offload_argv[i], '@')) - && p != offload_argv[i] - && sscanf (p, "@%li%n", &loffset, &consumed) >= 1 - && strlen (p) == (unsigned int) consumed) - { - filename = XNEWVEC (char, p - offload_argv[i] + 1); - memcpy (filename, offload_argv[i], p - offload_argv[i]); - filename[p - offload_argv[i]] = '\0'; - file_offset = (off_t) loffset; - } - fd = open (filename, O_RDONLY | O_BINARY); - if (fd == -1) - fatal_error (input_location, "cannot open %s: %m", filename); - if (!find_and_merge_options (fd, file_offset, - OFFLOAD_SECTION_NAME_PREFIX, - &offload_fdecoded_options, - &offload_fdecoded_options_count, - collect_gcc)) - fatal_error (input_location, "cannot read %s: %m", filename); - close (fd); - if (filename != offload_argv[i]) - XDELETEVEC (filename); - } - - compile_images_for_offload_targets (num_offload_files, offload_argv, - offload_fdecoded_options, - offload_fdecoded_options_count, - decoded_options, - decoded_options_count); - - free_array_of_ptrs ((void **) offload_argv, num_offload_files); - - if (offload_names) - { - find_crtoffloadtable (); - for (i = 0; offload_names[i]; i++) - printf ("%s\n", offload_names[i]); - free_array_of_ptrs ((void **) offload_names, i); - } + if (!fgets (buf, piece, f)) + break; + len = strlen (filename); + if (filename[len - 1] != '\n') { + filename = XRESIZEVEC (char, filename, len + piece); + buf = filename + len; + goto cont1; + } + filename[len - 1] = '\0'; + offload_argv[i] = filename; + } + fclose (f); + if (offload_argv[num_offload_files - 1] == NULL) + fatal_error (input_location, "invalid format of %s", + offload_objects_file_name); + maybe_unlink (offload_objects_file_name); + offload_objects_file_name = NULL; + + /* Look at saved offload options in files. */ + for (i = 0; i < num_offload_files; i++) { + char *p; + long loffset; + int fd, consumed; + off_t file_offset = 0; + char *filename = offload_argv[i]; + + if ((p = strrchr (offload_argv[i], '@')) + && p != offload_argv[i] + && sscanf (p, "@%li%n", &loffset, &consumed) >= 1 + && strlen (p) == (unsigned int) consumed) { + + filename = XNEWVEC (char, p - offload_argv[i] + 1); + memcpy (filename, offload_argv[i], p - offload_argv[i]); + filename[p - offload_argv[i]] = '\0'; + file_offset = (off_t) loffset; + } + fd = open (filename, O_RDONLY | O_BINARY); + if (fd == -1) + fatal_error (input_location, "cannot open %s: %m", filename); + if (!find_and_merge_options (fd, file_offset, + OFFLOAD_SECTION_NAME_PREFIX, + &offload_fdecoded_options, + &offload_fdecoded_options_count, + collect_gcc)) + fatal_error (input_location, "cannot read %s: %m", filename); + close (fd); + if (filename != offload_argv[i]) + XDELETEVEC (filename); } + compile_images_for_offload_targets (num_offload_files, offload_argv, + offload_fdecoded_options, + offload_fdecoded_options_count, + decoded_options, + decoded_options_count); + + free_array_of_ptrs ((void **) offload_argv, num_offload_files); + + if (offload_names) { + find_crtoffloadtable (); + for (i = 0; offload_names[i]; i++) + printf ("%s\n", offload_names[i]); + free_array_of_ptrs ((void **) offload_names, i); + } + } + /* If object files contain offload sections, but do not contain LTO sections, then there is no need to perform a link-time recompilation, i.e. lto-wrapper is used only for a compilation of offload images. */ if (have_offload && !have_lto) goto finish; - if (lto_mode == LTO_MODE_LTO) - { - /* -dumpbase argument for LTO. */ - flto_out = concat (dumppfx, "lto.o", NULL); - obstack_ptr_grow (&argv_obstack, flto_out); - - if (!save_temps) - flto_out = make_temp_file (".lto.o"); - obstack_ptr_grow (&argv_obstack, "-o"); - obstack_ptr_grow (&argv_obstack, flto_out); + if (lto_mode == LTO_MODE_LTO) { + /* -dumpbase argument for LTO. */ + flto_out = concat (dumppfx, "lto.o", NULL); + obstack_ptr_grow (&argv_obstack, flto_out); + + if (!save_temps) + flto_out = make_temp_file (".lto.o"); + obstack_ptr_grow (&argv_obstack, "-o"); + obstack_ptr_grow (&argv_obstack, flto_out); + } + else { + const char *list_option = "-fltrans-output-list="; + + /* -dumpbase argument for WPA. */ + char *dumpbase = concat (dumppfx, "wpa", NULL); + obstack_ptr_grow (&argv_obstack, dumpbase); + + if (save_temps) + ltrans_output_file = concat (dumppfx, "ltrans.out", NULL); + else + ltrans_output_file = make_temp_file (".ltrans.out"); + list_option_full = concat (list_option, ltrans_output_file, NULL); + obstack_ptr_grow (&argv_obstack, list_option_full); + + if (jobserver) { + if (verbose) + fprintf (stderr, "Using make jobserver\n"); + obstack_ptr_grow (&argv_obstack, xstrdup ("-fwpa=jobserver")); } - else - { - const char *list_option = "-fltrans-output-list="; - - /* -dumpbase argument for WPA. */ - char *dumpbase = concat (dumppfx, "wpa", NULL); - obstack_ptr_grow (&argv_obstack, dumpbase); - - if (save_temps) - ltrans_output_file = concat (dumppfx, "ltrans.out", NULL); - else - ltrans_output_file = make_temp_file (".ltrans.out"); - list_option_full = concat (list_option, ltrans_output_file, NULL); - obstack_ptr_grow (&argv_obstack, list_option_full); - - if (jobserver) - { - if (verbose) - fprintf (stderr, "Using make jobserver\n"); - obstack_ptr_grow (&argv_obstack, xstrdup ("-fwpa=jobserver")); - } - else if (auto_parallel) - { - char buf[256]; - init_num_threads (); - if (verbose) - fprintf (stderr, "LTO parallelism level set to %ld\n", - nthreads_var); - sprintf (buf, "-fwpa=%ld", nthreads_var); - obstack_ptr_grow (&argv_obstack, xstrdup (buf)); - } - else if (parallel > 1) - { - char buf[256]; - sprintf (buf, "-fwpa=%i", parallel); - obstack_ptr_grow (&argv_obstack, xstrdup (buf)); - } - else - obstack_ptr_grow (&argv_obstack, "-fwpa"); + else if (auto_parallel) { + char buf[256]; + init_num_threads (); + if (verbose) + fprintf (stderr, "LTO parallelism level set to %ld\n", + nthreads_var); + sprintf (buf, "-fwpa=%ld", nthreads_var); + obstack_ptr_grow (&argv_obstack, xstrdup (buf)); + } + else if (parallel > 1) { + char buf[256]; + sprintf (buf, "-fwpa=%i", parallel); + obstack_ptr_grow (&argv_obstack, xstrdup (buf)); } + else + obstack_ptr_grow (&argv_obstack, "-fwpa"); + } /* Append input arguments. */ for (i = 0; i < lto_argc; ++i) @@ -1723,238 +1638,263 @@ cont1: files and append those to the partial link commandline. */ n_debugobj = 0; early_debug_object_names = NULL; - if (! skip_debug) - { - early_debug_object_names = XCNEWVEC (const char *, ltoobj_argc+ 1); - num_deb_objs = ltoobj_argc; - for (i = 0; i < ltoobj_argc; ++i) - { - const char *tem; - if ((tem = debug_objcopy (ltoobj_argv[i], !linker_output_rel))) - { - early_debug_object_names[i] = tem; - n_debugobj++; - } - } + if (!skip_debug) { + early_debug_object_names = XCNEWVEC (const char *, ltoobj_argc+ 1); + num_deb_objs = ltoobj_argc; + for (i = 0; i < ltoobj_argc; ++i) { + const char *tem; + if ((tem = debug_objcopy (ltoobj_argv[i], !linker_output_rel))) { + early_debug_object_names[i] = tem; + n_debugobj++; + } } + } - if (lto_mode == LTO_MODE_LTO) - { - printf ("%s\n", flto_out); - if (!skip_debug) - { - for (i = 0; i < ltoobj_argc; ++i) - if (early_debug_object_names[i] != NULL) - printf ("%s\n", early_debug_object_names[i]); - } - /* These now belong to collect2. */ - free (flto_out); - flto_out = NULL; - free (early_debug_object_names); - early_debug_object_names = NULL; + if (lto_mode == LTO_MODE_LTO) { + printf ("%s\n", flto_out); + if (!skip_debug) { + for (i = 0; i < ltoobj_argc; ++i) + if (early_debug_object_names[i] != NULL) + printf ("%s\n", early_debug_object_names[i]); } - else - { - FILE *stream = fopen (ltrans_output_file, "r"); - FILE *mstream = NULL; - struct obstack env_obstack; - int priority; - - if (!stream) - fatal_error (input_location, "%: %s: %m", ltrans_output_file); - - /* Parse the list of LTRANS inputs from the WPA stage. */ - obstack_init (&env_obstack); - nr = 0; - for (;;) - { - const unsigned piece = 32; - char *output_name = NULL; - char *buf, *input_name = (char *)xmalloc (piece); - size_t len; - - buf = input_name; - if (fscanf (stream, "%i\n", &priority) != 1) - { - if (!feof (stream)) - fatal_error (input_location, - "corrupted ltrans output file %s", - ltrans_output_file); - break; - } + /* These now belong to collect2. */ + free (flto_out); + flto_out = NULL; + free (early_debug_object_names); + early_debug_object_names = NULL; + } + else { + FILE *stream = fopen (ltrans_output_file, "r"); + FILE *mstream = NULL; + struct obstack env_obstack; + int priority; + + if (!stream) + fatal_error (input_location, "%: %s: %m", ltrans_output_file); + + /* Parse the list of LTRANS inputs from the WPA stage. */ + obstack_init (&env_obstack); + nr = 0; + for (;;) { + const unsigned piece = 32; + char *output_name = NULL; + char *buf, *input_name = (char *)xmalloc (piece); + size_t len; + + buf = input_name; + if (fscanf (stream, "%i\n", &priority) != 1) { + if (!feof (stream)) + fatal_error (input_location, + "corrupted ltrans output file %s", + ltrans_output_file); + break; + } cont: - if (!fgets (buf, piece, stream)) - break; - len = strlen (input_name); - if (input_name[len - 1] != '\n') - { - input_name = (char *)xrealloc (input_name, len + piece); - buf = input_name + len; - goto cont; - } - input_name[len - 1] = '\0'; - - if (input_name[0] == '*') - output_name = &input_name[1]; - - nr++; - ltrans_priorities - = (int *)xrealloc (ltrans_priorities, nr * sizeof (int) * 2); - input_names = (char **)xrealloc (input_names, nr * sizeof (char *)); - output_names = (char **)xrealloc (output_names, nr * sizeof (char *)); - ltrans_priorities[(nr-1)*2] = priority; - ltrans_priorities[(nr-1)*2+1] = nr-1; - input_names[nr-1] = input_name; - output_names[nr-1] = output_name; - } - fclose (stream); - maybe_unlink (ltrans_output_file); - ltrans_output_file = NULL; - - if (parallel) - { - makefile = make_temp_file (".mk"); - mstream = fopen (makefile, "w"); - qsort (ltrans_priorities, nr, sizeof (int) * 2, cmp_priority); - } - - /* Execute the LTRANS stage for each input file (or prepare a - makefile to invoke this in parallel). */ - for (i = 0; i < nr; ++i) - { - char *output_name; - char *input_name = input_names[i]; - /* If it's a pass-through file do nothing. */ - if (output_names[i]) - continue; - - /* Replace the .o suffix with a .ltrans.o suffix and write - the resulting name to the LTRANS output list. */ - obstack_grow (&env_obstack, input_name, strlen (input_name) - 2); - obstack_grow (&env_obstack, ".ltrans.o", sizeof (".ltrans.o")); - output_name = XOBFINISH (&env_obstack, char *); - - /* Adjust the dumpbase if the linker output file was seen. */ - int dumpbase_len = (strlen (dumppfx) + sizeof (DUMPBASE_SUFFIX)); - char *dumpbase = (char *) xmalloc (dumpbase_len + 1); - snprintf (dumpbase, dumpbase_len, "%sltrans%u.ltrans", dumppfx, i); - argv_ptr[0] = dumpbase; - - argv_ptr[1] = "-fltrans"; - argv_ptr[2] = "-o"; - argv_ptr[3] = output_name; - argv_ptr[4] = input_name; - argv_ptr[5] = NULL; - if (parallel) - { - fprintf (mstream, "%s:\n\t@%s ", output_name, new_argv[0]); - for (j = 1; new_argv[j] != NULL; ++j) - fprintf (mstream, " '%s'", new_argv[j]); - fprintf (mstream, "\n"); - /* If we are not preserving the ltrans input files then - truncate them as soon as we have processed it. This - reduces temporary disk-space usage. */ - if (! save_temps) - fprintf (mstream, "\t@-touch -r %s %s.tem > /dev/null 2>&1 " - "&& mv %s.tem %s\n", - input_name, input_name, input_name, input_name); - } - else - { - fork_execute (new_argv[0], CONST_CAST (char **, new_argv), - true); - maybe_unlink (input_name); - } - - output_names[i] = output_name; - } - if (parallel) - { - struct pex_obj *pex; - char jobs[32]; - - fprintf (mstream, - ".PHONY: all\n" - "all:"); - for (i = 0; i < nr; ++i) - { - int j = ltrans_priorities[i*2 + 1]; - fprintf (mstream, " \\\n\t%s", output_names[j]); - } - fprintf (mstream, "\n"); - fclose (mstream); - if (!jobserver) - { - /* Avoid passing --jobserver-fd= and similar flags - unless jobserver mode is explicitly enabled. */ - putenv (xstrdup ("MAKEFLAGS=")); - putenv (xstrdup ("MFLAGS=")); - } - - char **make_argv = buildargv (getenv ("MAKE")); - if (make_argv) - { - for (unsigned argc = 0; make_argv[argc]; argc++) - obstack_ptr_grow (&argv_obstack, make_argv[argc]); - } - else - obstack_ptr_grow (&argv_obstack, "make"); - - obstack_ptr_grow (&argv_obstack, "-f"); - obstack_ptr_grow (&argv_obstack, makefile); - if (!jobserver) - { - snprintf (jobs, 31, "-j%ld", - auto_parallel ? nthreads_var : parallel); - obstack_ptr_grow (&argv_obstack, jobs); - } - obstack_ptr_grow (&argv_obstack, "all"); - obstack_ptr_grow (&argv_obstack, NULL); - new_argv = XOBFINISH (&argv_obstack, const char **); - - pex = collect_execute (new_argv[0], CONST_CAST (char **, new_argv), - NULL, NULL, PEX_SEARCH, false); - do_wait (new_argv[0], pex); - freeargv (make_argv); - maybe_unlink (makefile); - makefile = NULL; - for (i = 0; i < nr; ++i) - maybe_unlink (input_names[i]); - } + if (!fgets (buf, piece, stream)) + break; + len = strlen (input_name); + if (input_name[len - 1] != '\n') { + input_name = (char *)xrealloc (input_name, len + piece); + buf = input_name + len; + goto cont; + } + input_name[len - 1] = '\0'; + + if (input_name[0] == '*') + output_name = &input_name[1]; + + nr++; + ltrans_priorities + = (int *)xrealloc (ltrans_priorities, nr * sizeof (int) * 2); + input_names = (char **)xrealloc (input_names, nr * sizeof (char *)); + output_names = (char **)xrealloc (output_names, nr * sizeof (char *)); + ltrans_priorities[(nr-1)*2] = priority; + ltrans_priorities[(nr-1)*2+1] = nr-1; + input_names[nr-1] = input_name; + output_names[nr-1] = output_name; + } + fclose (stream); + maybe_unlink (ltrans_output_file); + ltrans_output_file = NULL; + + fprintf(stderr, "JOHN PRINT: parallel=%d\n", parallel); + + if (parallel) { + makefile = make_temp_file (".mk"); + fprintf(stderr, "JOHN PRINT: created mk\n"); + mstream = fopen (makefile, "w"); + qsort (ltrans_priorities, nr, sizeof (int) * 2, cmp_priority); + sleep(5); + } + + fprintf(stderr, "\tcreating mapper\n"); + auto *mapper = get_mapper (main_source_loc); + fprintf(stderr, "\tgot mapper\n"); + //char test[]="gcc test.c -o test"; + //mapper->Cork(); + //mapper->LTOCompile(test); + //mapper->Uncork(); + + + /* Execute the LTRANS stage for each input file (or prepare a + makefile to invoke this in parallel). */ + for (i = 0; i < nr; ++i) { + char *output_name; + char *input_name = input_names[i]; + /* If it's a pass-through file do nothing. */ + if (output_names[i]) + continue; + + /* Replace the .o suffix with a .ltrans.o suffix and write + the resulting name to the LTRANS output list. */ + obstack_grow (&env_obstack, input_name, strlen (input_name) - 2); + obstack_grow (&env_obstack, ".ltrans.o", sizeof (".ltrans.o")); + output_name = XOBFINISH (&env_obstack, char *); + + /* Adjust the dumpbase if the linker output file was seen. */ + int dumpbase_len = (strlen (dumppfx) + sizeof (DUMPBASE_SUFFIX)); + char *dumpbase = (char *) xmalloc (dumpbase_len + 1); + snprintf (dumpbase, dumpbase_len, "%sltrans%u.ltrans", dumppfx, i); + argv_ptr[0] = dumpbase; + + argv_ptr[1] = "-fltrans"; + argv_ptr[2] = "-o"; + argv_ptr[3] = output_name; + argv_ptr[4] = input_name; + argv_ptr[5] = NULL; + if (parallel) { + fprintf (mstream, "%s:\n\t@%s ", output_name, new_argv[0]); + for (j = 1; new_argv[j] != NULL; ++j) + fprintf (mstream, " '%s'", new_argv[j]); + fprintf (mstream, "\n"); + /* If we are not preserving the ltrans input files then + truncate them as soon as we have processed it. This + reduces temporary disk-space usage. */ + if (! save_temps) + fprintf (mstream, "\t@-touch -r %s %s.tem > /dev/null 2>&1 " + "&& mv %s.tem %s\n", + input_name, input_name, input_name, input_name); + } + else { + + fprintf(stderr, "\tRAVI PRINT: %s ", new_argv[0]); + + mapper->Cork(); + mapper->LTOCompile(new_argv[0]); + + const char **arg_it = new_argv + 1; + + while(*arg_it != NULL) { + //fprintf(stderr, "\tRAVI PRINT: %s\n", *arg_it); + fprintf(stderr, " \'%s\' ", *arg_it); + mapper->LTOCompile(*arg_it); + arg_it++; + } + fprintf(stderr, "\n"); + + mapper->Uncork(); + + //fprintf(stderr, "\tRAVI PRINT: %s\n", new_argv[0]); + fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true); + maybe_unlink (input_name); + } + + output_names[i] = output_name; + } + if (parallel) { + struct pex_obj *pex; + char jobs[32]; + + fprintf (mstream, + ".PHONY: all\n" + "all:"); + for (i = 0; i < nr; ++i) { + int j = ltrans_priorities[i*2 + 1]; + fprintf (mstream, " \\\n\t%s", output_names[j]); + } + fprintf (mstream, "\n"); + fclose (mstream); + fprintf(stderr, "JOHN PRINT: go check mstream!\n"); + + sleep(60); + if (!jobserver) { + /* Avoid passing --jobserver-fd= and similar flags + unless jobserver mode is explicitly enabled. */ + putenv (xstrdup ("MAKEFLAGS=")); + putenv (xstrdup ("MFLAGS=")); + } + + char **make_argv = buildargv (getenv ("MAKE")); + if (make_argv) { + for (unsigned argc = 0; make_argv[argc]; argc++) + obstack_ptr_grow (&argv_obstack, make_argv[argc]); + } + else + obstack_ptr_grow (&argv_obstack, "make"); + + obstack_ptr_grow (&argv_obstack, "-f"); + obstack_ptr_grow (&argv_obstack, makefile); + if (!jobserver) { + snprintf (jobs, 31, "-j%ld", + auto_parallel ? nthreads_var : parallel); + obstack_ptr_grow (&argv_obstack, jobs); + } + obstack_ptr_grow (&argv_obstack, "all"); + obstack_ptr_grow (&argv_obstack, NULL); + new_argv = XOBFINISH (&argv_obstack, const char **); + + pex = collect_execute (new_argv[0], CONST_CAST (char **, new_argv), + NULL, NULL, PEX_SEARCH, false); + do_wait (new_argv[0], pex); + freeargv (make_argv); + maybe_unlink (makefile); + makefile = NULL; for (i = 0; i < nr; ++i) - { - fputs (output_names[i], stdout); - putc ('\n', stdout); - free (input_names[i]); - } - if (!skip_debug) - { - for (i = 0; i < ltoobj_argc; ++i) - if (early_debug_object_names[i] != NULL) - printf ("%s\n", early_debug_object_names[i]); - } - nr = 0; - free (ltrans_priorities); - free (output_names); - output_names = NULL; - free (early_debug_object_names); - early_debug_object_names = NULL; - free (input_names); - free (list_option_full); - obstack_free (&env_obstack, NULL); + maybe_unlink (input_names[i]); } - finish: + for (i = 0; i < nr; ++i) { + fputs (output_names[i], stdout); + putc ('\n', stdout); + free (input_names[i]); + } + if (!skip_debug) { + for (i = 0; i < ltoobj_argc; ++i) + if (early_debug_object_names[i] != NULL) + printf ("%s\n", early_debug_object_names[i]); + } + nr = 0; + free (ltrans_priorities); + free (output_names); + output_names = NULL; + free (early_debug_object_names); + early_debug_object_names = NULL; + free (input_names); + free (list_option_full); + obstack_free (&env_obstack, NULL); + } + +finish: XDELETE (lto_argv); obstack_free (&argv_obstack, NULL); } /* Entry point. */ +int main (int argc, char *argv[]) { + fprintf(stderr, "JOHN PRINT: lto-wrapper\n"); + + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) != NULL) { + fprintf(stderr, "Current working dir: %s\n", cwd); + } else { + perror("getcwd() error"); + return 1; + } + fflush(stderr); + + sleep(5); -int -main (int argc, char *argv[]) -{ const char *p; init_opts_obstack (); diff --git a/gcc/mapper-client.cc b/gcc/mapper-client.cc new file mode 100644 index 000000000000..5b36bc5866c5 --- /dev/null +++ b/gcc/mapper-client.cc @@ -0,0 +1,331 @@ +/* C++ modules. Experimental! + Copyright (C) 2017-2020 Free Software Foundation, Inc. + Written by Nathan Sidwell while at FaceBook + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" + +#include "line-map.h" +#include "diagnostic-core.h" +#define MAPPER_FOR_GCC 1 +#include "mapper.h" + +module_client::module_client (pex_obj *p, int fd_from, int fd_to) + : Client (fd_from, fd_to), pex (p) +{ +} + +static module_client * +spawn_mapper_program (char const **errmsg, std::string &name, + char const *full_program_name) +{ + /* Split writable at white-space. No space-containing args for + you! */ + // At most every other char could be an argument + char **argv = new char *[name.size () / 2 + 2]; + unsigned arg_no = 0; + char *str = new char[name.size ()]; + memcpy (str, name.c_str () + 1, name.size ()); + + for (auto ptr = str; ; ++ptr) + { + while (*ptr == ' ') + ptr++; + if (!*ptr) + break; + argv[arg_no++] = ptr; + while (*ptr && *ptr != ' ') + ptr++; + if (!*ptr) + break; + *ptr = 0; + } + argv[arg_no] = nullptr; + + auto *pex = pex_init (PEX_USE_PIPES, progname, NULL); + FILE *to = pex_input_pipe (pex, false); + name = argv[0]; + if (!to) + *errmsg = "connecting input"; + else + { + int flags = PEX_SEARCH; + + if (full_program_name) + { + /* Prepend the invoking path. */ + size_t dir_len = progname - full_program_name; + std::string argv0; + argv0.reserve (dir_len + name.size ()); + argv0.append (full_program_name, dir_len).append (name); + name = std::move (argv0); + argv[0] = const_cast (name.c_str ()); + flags = 0; + } + int err; + *errmsg = pex_run (pex, flags, argv[0], argv, NULL, NULL, &err); + } + delete[] str; + delete[] argv; + + int fd_from = -1, fd_to = -1; + if (!*errmsg) + { + FILE *from = pex_read_output (pex, false); + if (from && (fd_to = dup (fileno (to))) >= 0) + fd_from = fileno (from); + else + *errmsg = "connecting output"; + fclose (to); + } + + if (*errmsg) + { + pex_free (pex); + return nullptr; + } + + return new module_client (pex, fd_from, fd_to); +} + +module_client * +module_client::open_module_client (location_t loc, const char *o, + void (*set_repo) (const char *), + char const *full_program_name) +{ + fprintf(stderr, "\tinside open_module_client\n"); + + module_client *c = nullptr; + std::string ident; + std::string name; + char const *errmsg = nullptr; + + if (o && o[0]) + { + fprintf(stderr, "\tmaybe a local or ipv6 address\n"); + /* Maybe a local or ipv6 address. */ + name = o; + auto last = name.find_last_of ('?'); + if (last != name.npos) + { + ident = name.substr (last + 1); + name.erase (last); + } + + if (name.size ()) + { + switch (name[0]) + { + case '<': + // to or <>fromto, or <> + { + int fd_from = -1, fd_to = -1; + char const *ptr = name.c_str (); + char *eptr; + + fd_from = strtoul (++ptr, &eptr, 0); + if (*eptr == '>') + { + ptr = eptr; + fd_to = strtoul (++ptr, &eptr, 0); + if (eptr != ptr && ptr == name.c_str () + 1) + fd_from = fd_to; + } + + if (*eptr) + errmsg = "parsing"; + else + { + if (name.size () == 2) + { + fd_from = fileno (stdin); + fd_to = fileno (stdout); + } + c = new module_client (fd_from, fd_to); + } + } + break; + + case '=': + // =localsocket + { + int fd = Cody::OpenLocal (&errmsg, name.c_str () + 1); + if (fd >= 0) + c = new module_client (fd, fd); + } + break; + + case '|': + // |program and args + c = spawn_mapper_program (&errmsg, name, full_program_name); + break; + + default: + // file or hostname:port + { + auto colon = name.find_last_of (':'); + if (colon != name.npos) + { + char const *cptr = name.c_str () + colon; + char *endp; + unsigned port = strtoul (cptr + 1, &endp, 10); + + if (port && endp != cptr + 1 && !*endp) + { + name[colon] = 0; + int fd = Cody::OpenInet6 (&errmsg, name.c_str (), port); + name[colon] = ':'; + + if (fd >= 0) + c = new module_client (fd, fd); + } + } + + } + break; + } + } + } + + if (!c) + { + fprintf(stderr, "\tmake a default in-process client\n"); + + // Make a default in-process client + auto r = new module_resolver (); + fprintf(stderr, "\tcreated module_resolver\n"); + auto *s = new Cody::Server (r); + fprintf(stderr, "\tcreated Cody::Server\n"); + c = new module_client (s); + fprintf(stderr, "\tcreated module_client\n"); + if (!errmsg && !name.empty ()) + { + int fd = open (name.c_str (), O_RDONLY | O_CLOEXEC); + if (fd < 0) + errmsg = "opening"; + else + { + if (r->read_tuple_file (fd, ident, false)) + errmsg = "reading"; + + close (fd); + } + } + else + { + r->set_repo ("gcm.cache"); + r->set_default (true); + } + } + +#ifdef SIGPIPE + fprintf(stderr, "\tSIGPIPE\n"); + if (!c->IsDirect ()) { + /* We need to ignore sig pipe for a while. */ + c->sigpipe = signal (SIGPIPE, SIG_IGN); + } +#endif + + fprintf(stderr, "\tcheck for errmsg\n"); + if (errmsg) { + fprintf(stderr, "\terror! %s mapper %s\n", errmsg, name.c_str()); + error_at (loc, "failed %s mapper %qs", errmsg, name.c_str ()); + } + fprintf(stderr, "\tc->Cork\n"); + + // now wave hello! + c->Cork (); + fprintf(stderr, "\tc->Connect\n"); + + c->Connect (std::string ("GCC"), ident); + //if( c->Connect("TEST" , "IDENT").GetCode() != Client::PC_CORKED ) { + // fprintf(stderr, "\tclient is not corked!!\n"); + //} + + //fprintf(stderr, "\tc->ModuleRepo\n"); + + //c->ModuleRepo (); + fprintf(stderr, "\tc->Uncork\n"); + + auto packets = c->Uncork (); + + fprintf(stderr, "\ttime to look at the packets..\n"); + + auto &connect = packets[0]; + if (connect.GetCode () == Cody::Client::PC_CONNECT) { + fprintf(stderr, "\tgood PC_CONNECT\n"); + } + else if (connect.GetCode () == Cody::Client::PC_ERROR) { + fprintf(stderr, "\tfailed PC_ERROR\n"); + error_at (loc, "failed mapper handshake %s", connect.GetString ().c_str ()); + } + + //auto &repo = packets[1]; + //if (repo.GetCode () == Cody::Client::PC_MODULE_REPO) + // set_repo (repo.GetString ().c_str ()); + + return c; +} + +void +module_client::close_module_client (location_t loc, module_client *mapper) +{ + if (mapper->IsDirect ()) + { + auto *s = mapper->GetServer (); + auto *r = s->GetResolver (); + delete s; + delete r; + } + else + { + if (mapper->pex) + { + int fd_write = mapper->GetFDWrite (); + if (fd_write >= 0) + close (fd_write); + + int status; + pex_get_status (mapper->pex, 1, &status); + + pex_free (mapper->pex); + mapper->pex = NULL; + + if (WIFSIGNALED (status)) + error_at (loc, "mapper died by signal %s", + strsignal (WTERMSIG (status))); + else if (WIFEXITED (status) && WEXITSTATUS (status) != 0) + error_at (loc, "mapper exit status %d", + WEXITSTATUS (status)); + } + else + { + int fd_read = mapper->GetFDRead (); + close (fd_read); + } + +#ifdef SIGPIPE + // Restore sigpipe + if (mapper->sigpipe != SIG_IGN) + signal (SIGPIPE, mapper->sigpipe); +#endif + } + + delete mapper; +} diff --git a/gcc/mapper-resolver.cc b/gcc/mapper-resolver.cc new file mode 100644 index 000000000000..be4849d1dfb0 --- /dev/null +++ b/gcc/mapper-resolver.cc @@ -0,0 +1,246 @@ +/* C++ modules. Experimental! -*- c++-mode -*- + Copyright (C) 2017-2020 Free Software Foundation, Inc. + Written by Nathan Sidwell while at FaceBook + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "mapper.h" +// C++ +#include +// C +#include +// OS +#include +#include +#include +#include +#include + +module_resolver::module_resolver (bool def) + : provide_default (def) +{ + fprintf(stderr, "\t module_resolver constructor \n"); + +} + +module_resolver::~module_resolver () +{ + if (fd_repo >= 0) + close (fd_repo); +} + +bool +module_resolver::set_repo (std::string &&r, bool force) +{ + if (force || repo.empty ()) + { + repo = std::move (r); + force = true; + } + return force; +} + +bool +module_resolver::add_mapping (std::string &&module, std::string &&file, + bool force) +{ + auto res = map.emplace (std::move (module), std::move (file)); + if (res.second) + force = true; + else if (force) + res.first->second = std::move (file); + + return force; +} + +int +module_resolver::read_tuple_file (int fd, char const *prefix, bool force) +{ + struct stat stat; + if (fstat (fd, &stat) < 0) + return -errno; + + // Just Map the file, we're gonna read all of it, so no need for + // line buffering + void *buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) + return -errno; + + size_t prefix_len = prefix ? strlen (prefix) : 0; + unsigned lineno = 0; + + for (char const *begin = reinterpret_cast (buffer), + *end = begin + stat.st_size, *eol; + begin != end; begin = eol + 1) + { + lineno++; + eol = std::find (begin, end, '\n'); + if (eol == end) + // last line has no \n, ignore the line, you lose + break; + + auto *pos = begin; + bool pfx_search = prefix_len != 0; + + pfx_search: + while (*pos == ' ' || *pos == '\t') + pos++; + + auto *space = pos; + while (*space != '\n' && *space != ' ' && *space != '\t') + space++; + + if (pos == space) + // at end of line, nothing here + continue; + + if (pfx_search) + { + if (size_t (space - pos) == prefix_len + && std::equal (pos, space, prefix)) + pfx_search = false; + pos = space; + goto pfx_search; + } + + std::string module (pos, space); + while (*space == ' ' || *space == '\t') + space++; + std::string file (space, eol); + + if (module[0] == '$') + { + if (module == "$root") + set_repo (std::move (file)); + else + return lineno; + } + else + { + if (file.empty ()) + file = GetCMIName (module); + add_mapping (std::move (module), std::move (file), force); + } + } + + munmap (buffer, stat.st_size); + + return 0; +} + +char const * +module_resolver::GetCMISuffix () +{ + return "gcm"; +} + +module_resolver * +module_resolver::ConnectRequest (Cody::Server *s, unsigned version, + std::string &a, std::string &i) +{ + if (!version || version > Cody::Version) + s->ErrorResponse ("version mismatch"); + else if (a != "GCC") + // Refuse anything but GCC + ErrorResponse (s, std::string ("only GCC supported")); + else if (!ident.empty () && ident != i) + // Failed ident check + ErrorResponse (s, std::string ("bad ident")); + else + // Success! + s->ConnectResponse ("gcc"); + + return this; +} + +int +module_resolver::ModuleRepoRequest (Cody::Server *s) +{ + s->ModuleRepoResponse (repo); + return 0; +} + +int +module_resolver::cmi_response (Cody::Server *s, std::string &module) +{ + auto iter = map.find (module); + if (iter == map.end ()) + { + std::string file; + if (provide_default) + file = std::move (GetCMIName (module)); + auto res = map.emplace (module, file); + iter = res.first; + } + + if (iter->second.empty ()) + s->ErrorResponse ("no such module"); + else + s->ModuleCMIResponse (iter->second); + + return 0; +} + +int +module_resolver::ModuleExportRequest (Cody::Server *s, std::string &module) +{ + return cmi_response (s, module); +} + +int +module_resolver::ModuleImportRequest (Cody::Server *s, std::string &module) +{ + return cmi_response (s, module); +} + +int +module_resolver::IncludeTranslateRequest (Cody::Server *s, + std::string &include) +{ + auto iter = map.find (include); + if (iter == map.end ()) + { + // Not found, look for it + if (fd_repo == -1 && !repo.empty ()) + { + fd_repo = open (repo.c_str (), O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (fd_repo < 0) + fd_repo = -2; + } + + if (fd_repo >= 0 || repo.empty ()) + { + auto file = GetCMIName (include); + struct stat statbuf; + if (fstatat (repo.empty () ? AT_FDCWD : fd_repo, + file.c_str (), &statbuf, 0) < 0 + || !S_ISREG (statbuf.st_mode)) + // Mark as not present + file.clear (); + auto res = map.emplace (include, file); + iter = res.first; + } + } + + if (iter == map.end () || iter->second.empty ()) + s->IncludeTranslateResponse (false); + else + s->ModuleCMIResponse (iter->second); + + return 0; +} + diff --git a/gcc/mapper-server.cc b/gcc/mapper-server.cc new file mode 100644 index 000000000000..23b847fa3b79 --- /dev/null +++ b/gcc/mapper-server.cc @@ -0,0 +1,994 @@ +/* C++ modules. Experimental! + Copyright (C) 2018-2020 Free Software Foundation, Inc. + Written by Nathan Sidwell while at FaceBook + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "mapper.h" + +// C++ +#include +#include +// GCC +#define INCLUDE_VECTOR +#define INCLUDE_MAP +#define INCLUDE_SET + +// Network +/* Include network stuff first. Excitingly OSX10.14 uses bcmp here, which + we poison later! */ +#if defined (HAVE_AF_UNIX) || defined (HAVE_AF_INET6) +/* socket, bind, listen, accept{4} */ +# define NETWORKING 1 +# include +# ifdef HAVE_AF_UNIX +/* sockaddr_un */ +# include +# endif +# include +# ifdef HAVE_AF_INET6 +/* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons. */ +# include +# endif +#ifdef HAVE_INET_NTOP +/* inet_ntop. */ +#include +#endif +#endif +#ifndef HAVE_AF_INET6 +# define gai_strerror(X) "" +#endif + +// Excitingly Darwin uses bcmp in its network headers, and we poison +// that in our setup. +#include "system.h" +#include "version.h" +#include "intl.h" +#include + +// Select or epoll +#ifdef NETWORKING +#ifdef HAVE_EPOLL +/* epoll_create, epoll_ctl, epoll_pwait */ +#include +#endif +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) +/* pselect or select */ +#include +#endif +#endif + +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t) (int); +#endif + +struct netmask { + in6_addr addr; + unsigned bits; + + netmask (const in6_addr &a, unsigned b) + { + if (b > sizeof (in6_addr) * 8) + b = sizeof (in6_addr) * 8; + bits = b; + unsigned byte = (b + 7) / 8; + unsigned ix = 0; + for (ix = 0; ix < byte; ix++) + addr.s6_addr[ix] = a.s6_addr[ix]; + for (; ix != sizeof (in6_addr); ix++) + addr.s6_addr[ix] = 0; + if (b & 3) + addr.s6_addr[b/7] &= (255 << 8) >> (b & 3); + } + + bool includes (const in6_addr &a) const + { + unsigned byte = bits / 8; + for (unsigned ix = 0; ix != byte; ix++) + if (addr.s6_addr[ix] != a.s6_addr[ix]) + return false; + if (bits & 3) + if ((addr.s6_addr[byte] ^ a.s6_addr[byte]) >> (8 - (bits & 3))) + return false; + return true; + } +}; + +/* Netmask comparison. */ +struct netmask_cmp { + bool operator() (const netmask &a, const netmask &b) const + { + if (a.bits != b.bits) + return a.bits < b.bits; + for (unsigned ix = 0; ix != sizeof (in6_addr); ix++) + if (a.addr.s6_addr[ix] != b.addr.s6_addr[ix]) + return a.addr.s6_addr[ix] < b.addr.s6_addr[ix]; + return false; + } +}; + +typedef std::set netmask_set_t; +typedef std::vector netmask_vec_t; + +const char *progname; + +/* Speak thoughts out loud. */ +static bool flag_noisy = false; + +/* One and done. */ +static bool flag_one = false; + +/* Serialize connections. */ +static bool flag_sequential = false; + +/* Fallback to default if map file is unrewarding. */ +static bool flag_fallback = false; + +/* Root binary directory. */ +static const char *flag_root = "gcm.cache"; + +static netmask_set_t netmask_set; + +static netmask_vec_t accept_addrs; + +/* Strip out the source directory from FILE. */ + +static const char * +trim_src_file (const char *file) +{ + static const char me[] = __FILE__; + unsigned pos = 0; + + while (file[pos] == me[pos] && me[pos]) + pos++; + while (pos && !IS_DIR_SEPARATOR (me[pos-1])) + pos--; + + return file + pos; +} + +/* Die screaming. */ + +void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD +internal_error (const char *fmt, ...) +{ + fprintf (stderr, "%s:Internal error ", progname); + va_list args; + + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fprintf (stderr, "\n"); + + exit (FATAL_EXIT_CODE); +} + +/* Hooked to from gcc_assert & gcc_unreachable. */ + +void ATTRIBUTE_NORETURN ATTRIBUTE_COLD +fancy_abort (const char *file, int line, const char *func) +{ + internal_error ("in %s, at %s:%d", func, trim_src_file (file), line); +} + +/* Exploded on a signal. */ + +static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD +crash_signal (int sig) +{ + signal (sig, SIG_DFL); + internal_error ("signal %s", strsignal (sig)); +} + +/* A fatal error of some kind. */ + +static void ATTRIBUTE_NORETURN ATTRIBUTE_COLD ATTRIBUTE_PRINTF_1 +error (const char *msg, ...) +{ + fprintf (stderr, "%s:error: ", progname); + va_list args; + + va_start (args, msg); + vfprintf (stderr, msg, args); + va_end (args); + fprintf (stderr, "\n"); + + exit (1); +} + +/* Progress messages to the user. */ +static bool ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD +noisy (const char *fmt, ...) +{ + fprintf (stderr, "%s:", progname); + va_list args; + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fprintf (stderr, "\n"); + + return false; +} + +/* More messages to the user. */ + +static void ATTRIBUTE_PRINTF_2 +fnotice (FILE *file, const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vfprintf (file, _(fmt), args); + va_end (args); +} + +static void ATTRIBUTE_NORETURN +print_usage (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; + + fnotice (file, "Usage: cxx-mapper [OPTION...] [CONNECTION] [MAPPINGS...] \n\n"); + fnotice (file, "C++ Module Mapper.\n\n"); + fnotice (file, " -a, --accept Netmask to accept from\n"); + fnotice (file, " -f, --fallback Use fallback for missing mappings\n"); + fnotice (file, " -h, --help Print this help, then exit\n"); + fnotice (file, " -n, --noisy Print progress messages\n"); + fnotice (file, " -1, --one One connection and then exit\n"); + fnotice (file, " -r, --root DIR Root compiled module directory\n"); + fnotice (file, " -s, --sequential Process connections sequentially\n"); + fnotice (file, " -v, --version Print version number, then exit\n"); + fnotice (file, "Send SIGTERM(%d) to terminate\n", SIGTERM); + fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", + bug_report_url); + exit (status); +} + +/* Print version information and exit. */ + +static void ATTRIBUTE_NORETURN +print_version (void) +{ + fnotice (stdout, "cxx-mapper %s%s\n", pkgversion_string, version_string); + fprintf (stdout, "Copyright %s 2018-2020 Free Software Foundation, Inc.\n", + _("(C)")); + fnotice (stdout, + _("This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or \n" + "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); + exit (SUCCESS_EXIT_CODE); +} + +/* ARG is a netmask to accept from. Add it to the table. Return + false if we fail to resolve it. */ + +static bool +accept_from (char *arg) +{ + bool ok = true; +#if HAVE_AF_INET6 + unsigned bits = sizeof (in6_addr) * 8; + char *slash = strrchr (arg, '/'); + if (slash) + { + *slash = 0; + if (slash[1]) + { + char *endp; + bits = strtoul (slash + 1, &endp, 0); + } + } + + addrinfo hints; + + hints.ai_flags = AI_NUMERICSERV; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_addrlen = 0; + hints.ai_addr = NULL; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + + struct addrinfo *addrs = NULL; + if (int e = getaddrinfo (slash == arg ? NULL : arg, "0", &hints, &addrs)) + { + noisy ("cannot resolve '%s': %s", arg, gai_strerror (e)); + ok = false; + } + else + for (addrinfo *next = addrs; next; next = next->ai_next) + if (next->ai_family == AF_INET6) + { + netmask mask (((const sockaddr_in6 *)next->ai_addr)->sin6_addr, bits); + netmask_set.insert (mask); + } + freeaddrinfo (addrs); +#endif + return ok; +} + +/* Process args, return index to first non-arg. */ + +static int +process_args (int argc, char **argv) +{ + static const struct option options[] = + { + { "accept", required_argument, NULL, 'a' }, + { "fallback",no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "noisy", no_argument, NULL, 'n' }, + { "one", no_argument, NULL, '1' }, + { "root", required_argument, NULL, 'r' }, + { "sequential", no_argument, NULL, 's' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } + }; + int opt; + bool bad_accept = false; + const char *opts = "a:fhn1r:sv"; + while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1) + { + switch (opt) + { + case 'a': + if (!accept_from (optarg)) + bad_accept = true; + break; + case 'f': + flag_fallback = true; + break; + case 'h': + print_usage (false); + /* print_usage will exit. */ + case 'n': + flag_noisy = true; + break; + case '1': + flag_one = true; + break; + case 'r': + flag_root = optarg; + break; + case 's': + flag_sequential = true; + break; + case 'v': + print_version (); + /* print_version will exit. */ + default: + print_usage (true); + /* print_usage will exit. */ + } + } + + if (bad_accept) + error ("failed to resolve all accept addresses"); + + return optind; +} + +/* Manipulate the EPOLL state, or do nothing, if there is epoll. */ + +#ifdef HAVE_EPOLL +static inline void +do_epoll_ctl (int epoll_fd, int code, int event, int fd, unsigned data) +{ + epoll_event ev; + ev.events = event; + ev.data.u32 = data; + if (epoll_ctl (epoll_fd, code, fd, &ev)) + { + noisy ("epoll_ctl error:%s", xstrerror (errno)); + gcc_unreachable (); + } +} +#define my_epoll_ctl(EFD,C,EV,FD,CL) \ + ((EFD) >= 0 ? do_epoll_ctl (EFD,C,EV,FD,CL) : (void)0) +#else +#define my_epoll_ctl(EFD,C,EV,FD,CL) ((void)(EFD), (void)(FD), (void)(CL)) +#endif + +#ifdef NETWORKING +/* We increment this to tell the server to shut down. */ +static volatile int term = false; +static volatile int kill_sock_fd = -1; +#if !defined (HAVE_PSELECT) && defined (HAVE_SELECT) +static int term_pipe[2] = {-1, -1}; +#else +#define term_pipe ((int *)NULL) +#endif + +/* A terminate signal. Shutdown gracefully. */ + +static void +term_signal (int sig) +{ + signal (sig, term_signal); + term = term + 1; + if (term_pipe && term_pipe[1] >= 0) + write (term_pipe[1], &term_pipe[1], 1); +} + +/* A kill signal. Shutdown immediately. */ + +static void +kill_signal (int sig) +{ + signal (sig, SIG_DFL); + int sock_fd = kill_sock_fd; + if (sock_fd >= 0) + close (sock_fd); + exit (2); +} + +bool process_server (Cody::Server *server, unsigned slot, int epoll_fd) +{ + switch (server->GetDirection ()) + { + case Cody::Server::READING: + if (int err = server->Read ()) + return !(err == EINTR || err == EAGAIN); + server->ProcessRequests (); + server->PrepareToWrite (); + break; + + case Cody::Server::WRITING: + if (int err = server->Write ()) + return !(err == EINTR || err == EAGAIN); + server->PrepareToRead (); + break; + + default: + // We should never get here + return true; + } + + // We've changed direction, so update epoll + gcc_assert (server->GetFDRead () == server->GetFDWrite ()); + my_epoll_ctl (epoll_fd, EPOLL_CTL_MOD, + server->GetDirection () == Cody::Server::READING + ? EPOLLIN : EPOLLOUT, server->GetFDRead (), slot + 1); + + return false; +} + +void close_server (Cody::Server *server, int epoll_fd) +{ + my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, server->GetFDRead (), 0); + + close (server->GetFDRead ()); + + delete server; +} + +int open_server (bool ip6, int sock_fd) +{ + sockaddr_in6 addr; + socklen_t addr_len = sizeof (addr); + +#ifdef HAVE_ACCEPT4 + int client_fd = accept4 (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, + &addr_len, SOCK_NONBLOCK); +#else + int client_fd = accept (sock_fd, ip6 ? (sockaddr *)&addr : nullptr, &addr_len); +#endif + if (client_fd < 0) + { + error ("cannot accept: %s", xstrerror (errno)); + flag_one = true; + } + else if (ip6) + { + const char *str = NULL; +#if HAVE_INET_NTOP + char name[INET6_ADDRSTRLEN]; + str = inet_ntop (addr.sin6_family, &addr.sin6_addr, name, sizeof (name)); +#endif + if (!accept_addrs.empty ()) + { + netmask_vec_t::iterator e = accept_addrs.end (); + for (netmask_vec_t::iterator i = accept_addrs.begin (); + i != e; ++i) + if (i->includes (addr.sin6_addr)) + goto present; + close (client_fd); + client_fd = -1; + noisy ("Rejecting connection from disallowed source '%s'", + str ? str : ""); + present:; + } + if (client_fd >= 0) + flag_noisy && noisy ("Accepting connection from '%s'", str ? str : ""); + } + + return client_fd; +} + +/* A server listening on bound socket SOCK_FD. */ + +static void +server (bool ipv6, int sock_fd, module_resolver *resolver) +{ + int epoll_fd = -1; + + signal (SIGTERM, term_signal); +#ifdef HAVE_EPOLL + epoll_fd = epoll_create (1); +#endif + if (epoll_fd >= 0) + my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0); + +#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT) + sigset_t mask; + { + sigset_t block; + sigemptyset (&block); + sigaddset (&block, SIGTERM); + sigprocmask (SIG_BLOCK, &block, &mask); + } +#endif + +#ifdef HAVE_EPOLL + const unsigned max_events = 20; + epoll_event events[max_events]; +#endif +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + fd_set readers, writers; +#endif + if (term_pipe) + pipe (term_pipe); + + // We need stable references to servers, so this array can contain nulls + std::vector connections; + unsigned live = 0; + fprintf(stderr, "\tabout to go into a wait loop\n"); + while (sock_fd >= 0 || live) + { + /* Wait for one or more events. */ + bool eintr = false; + int event_count; + + if (epoll_fd >= 0) + { +#ifdef HAVE_EPOLL + event_count = epoll_pwait (epoll_fd, events, max_events, -1, &mask); +#endif + } + else + { +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + FD_ZERO (&readers); + FD_ZERO (&writers); + + unsigned limit = 0; + if (sock_fd >= 0 + && !(term || (live && (flag_one || flag_sequential)))) + { + FD_SET (sock_fd, &readers); + limit = sock_fd + 1; + } + + if (term_pipe && term_pipe[0] >= 0) + { + FD_SET (term_pipe[0], &readers); + if (unsigned (term_pipe[0]) >= limit) + limit = term_pipe[0] + 1; + } + + for (auto iter = connections.begin (); + iter != connections.end (); ++iter) + if (auto *server = *iter) + { + int fd = -1; + switch (server->GetDirection ()) + { + case Cody::Server::READING: + fd = server->GetFDRead (); + FD_SET (fd, &readers); + break; + case Cody::Server::WRITING: + fd = server->GetFDWrite (); + FD_SET (fd, &writers); + break; + default: + break; + } + + if (fd >= 0 && limit <= unsigned (fd)) + limit = fd + 1; + } + +#ifdef HAVE_PSELECT + event_count = pselect (limit, &readers, &writers, NULL, NULL, &mask); +#else + event_count = select (limit, &readers, &writers, NULL, NULL); +#endif + if (term_pipe && FD_ISSET (term_pipe[0], &readers)) + { + /* Fake up an interrupted system call. */ + event_count = -1; + errno = EINTR; + } +#endif + } + + if (event_count < 0) + { + // Error in waiting + if (errno == EINTR) + { + flag_noisy && noisy ("Interrupted wait"); + eintr = true; + } + else + error ("cannot %s: %s", epoll_fd >= 0 ? "epoll_wait" +#ifdef HAVE_PSELECT + : "pselect", +#else + : "select", +#endif + xstrerror (errno)); + event_count = 0; + } + + auto iter = connections.begin (); + while (event_count--) + { + // Process an event + int active = -2; + + if (epoll_fd >= 0) + { +#ifdef HAVE_EPOLL + /* See PR c++/88664 for why a temporary is used. */ + unsigned data = events[event_count].data.u32; + active = int (data) - 1; +#endif + } + else + { + for (; iter != connections.end (); ++iter) + if (auto *server = *iter) + { + bool found = false; + switch (server->GetDirection ()) + { +#if defined (HAVE_PSELECT) || defined (HAVE_SELECT) + case Cody::Server::READING: + found = FD_ISSET (server->GetFDRead (), &readers); + break; + case Cody::Server::WRITING: + found = FD_ISSET (server->GetFDWrite (), &writers); + break; +#endif + default: + break; + } + + if (found) + { + active = iter - connections.begin (); + ++iter; + break; + } + } + + if (active < 0 && sock_fd >= 0 && FD_ISSET (sock_fd, &readers)) + active = -1; + } + + if (active >= 0) + { + // Do the action + auto *server = connections[active]; + if (process_server (server, active, epoll_fd)) + { + connections[active] = nullptr; + close_server (server, epoll_fd); + live--; + if (flag_sequential) + my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, sock_fd, 0); + } + } + else if (active == -1 && !eintr) + { + // New connection + int fd = open_server (ipv6, sock_fd); + if (fd >= 0) + { +#if !defined (HAVE_ACCEPT4) \ + && (defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT)) + int flags = fcntl (fd, F_GETFL, 0); + fcntl (fd, F_SETFL, flags | O_NONBLOCK); +#endif + auto *server = new Cody::Server (resolver, fd); + + unsigned slot = connections.size (); + if (live == slot) + connections.push_back (server); + else + for (auto iter = connections.begin (); ; ++iter) + if (!*iter) + { + *iter = server; + slot = iter - connections.begin (); + break; + } + live++; + my_epoll_ctl (epoll_fd, EPOLL_CTL_ADD, EPOLLIN, fd, slot + 1); + } + } + + if (sock_fd >= 0 + && (term || (live && (flag_one || flag_sequential)))) + { + /* Stop paying attention to sock_fd. */ + my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, 0); + if (flag_one || term) + { + close (sock_fd); + sock_fd = -1; + } + } + } + } +#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT) + /* Restore the signal mask. */ + sigprocmask (SIG_SETMASK, &mask, NULL); +#endif + + gcc_assert (sock_fd < 0); + if (epoll_fd >= 0) + close (epoll_fd); + + if (term_pipe && term_pipe[0] >= 0) + { + close (term_pipe[0]); + close (term_pipe[1]); + } +} + +#if 0 + + + if (eintr) + flag_noisy && noisy ("Interrupted wait"); + + + if (sock_fd >= 0 && (term || flag_one || flag_sequential)) + { + /* Stop paying attention to sock_fd. */ + my_epoll_ctl (epoll_fd, EPOLL_CTL_DEL, EPOLLIN, sock_fd, NULL); + if (flag_one || term) + { + close (sock_fd); + sock_fd = -1; + } + } + } + +#if defined (HAVE_EPOLL) || defined (HAVE_PSELECT) || defined (HAVE_SELECT) + /* Restore the signal mask. */ + sigprocmask (SIG_SETMASK, &mask, NULL); +#endif + + gcc_assert (sock_fd < 0); + if (epoll_fd >= 0) + close (epoll_fd); + + if (term_pipe && term_pipe[0] >= 0) + { + close (term_pipe[0]); + close (term_pipe[1]); + } +} +#endif + +#endif + +static int maybe_parse_socket (std::string &option, module_resolver *r) +{ + /* Local or ipv6 address. */ + auto last = option.find_last_of ('?'); + if (last != option.npos) + { + r->set_ident (option.c_str () + last + 1); + option.erase (last); + } + int fd = -2; + char const *errmsg = nullptr; + + /* Does it look like a socket? */ + if (option[0] == '=') + /* A local socket. */ + fd = Cody::ListenLocal (&errmsg, option.c_str () + 1); + else + { + auto colon = option.find_last_of (':'); + if (colon != option.npos) + { + /* Try a hostname:port address. */ + char const *cptr = option.c_str () + colon; + char *endp; + unsigned port = strtoul (cptr + 1, &endp, 10); + + if (port && endp != cptr + 1 && !*endp) + { + /* Ends in ':number', treat as ipv6 domain socket. */ + option.erase (colon); + fd = Cody::ListenInet6 (&errmsg, option.c_str (), port); + } + } + } + + if (errmsg) + error ("failed to open socket: %s", errmsg); + + return fd; +} + +int +main (int argc, char *argv[]) +{ + fprintf(stderr, "\tmapper-server main\n"); + + const char *p = argv[0] + strlen (argv[0]); + while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) + --p; + progname = p; + + xmalloc_set_program_name (progname); + fprintf(stderr, "\tsignals\n"); + +#ifdef SIGSEGV + signal (SIGSEGV, crash_signal); +#endif +#ifdef SIGILL + signal (SIGILL, crash_signal); +#endif +#ifdef SIGBUS + signal (SIGBUS, crash_signal); +#endif +#ifdef SIGABRT + signal (SIGABRT, crash_signal); +#endif +#ifdef SIGFPE + signal (SIGFPE, crash_signal); +#endif +#ifdef SIGPIPE + /* Ignore sigpipe, so read/write get an error. */ + signal (SIGPIPE, SIG_IGN); +#endif +#ifdef SIGINT + signal (SIGINT, kill_signal); +#endif + + int argno = process_args (argc, argv); + + module_resolver r; + fprintf(stderr, "\tgot a module_resolver\n"); + if (flag_root) + r.set_repo (flag_root); + + std::string name; + int sock_fd = -1; /* Socket fd, otherwise stdin/stdout. */ + + fprintf(stderr, "\tneed a sock_fd\n"); + if (argno != argc) + { + name = argv[argno]; + sock_fd = maybe_parse_socket (name, &r); + if (!name.empty ()) + argno++; + } + fprintf(stderr, "\tsock_fd? = %d\n", sock_fd); + + if (argno != argc) + for (; argno != argc; argno++) + { + std::string option = argv[argno]; + char const *prefix = nullptr; + auto ident = option.find_last_of ('?'); + if (ident != option.npos) + { + prefix = option.c_str () + ident + 1; + option[ident] = 0; + } + int fd = open (option.c_str (), O_RDONLY | O_CLOEXEC); + int err = 0; + if (fd < 0) + err = errno; + else + { + err = r.read_tuple_file (fd, prefix, false); + close (fd); + } + + if (err) + error ("failed reading '%s': %s", option.c_str (), xstrerror (err)); + } + else + flag_fallback = true; + r.set_default (flag_fallback); + + fprintf(stderr, "\tnetmask stuff\n"); +#ifdef HAVE_AF_INET6 + netmask_set_t::iterator end = netmask_set.end (); + for (netmask_set_t::iterator iter = netmask_set.begin (); + iter != end; ++iter) + { + netmask_vec_t::iterator e = accept_addrs.end (); + for (netmask_vec_t::iterator i = accept_addrs.begin (); i != e; ++i) + if (i->includes (iter->addr)) + goto present; + accept_addrs.push_back (*iter); + present:; + } +#endif + +#ifdef NETWORKING + if (sock_fd >= 0) + { + fprintf(stderr, "\tstarting a server\n"); + server (name[0] != '=', sock_fd, &r); + fprintf(stderr, "\tgot a server\n"); + if (name[0] == '=') + unlink (name.c_str () + 1); + fprintf(stderr, "\t name: %s \n", name.c_str()); + } + else +#endif + { + fprintf(stderr, "\tabout to loop infinitely\n"); + + + gcc_assert (sock_fd < 0); + auto server = Cody::Server (&r, 0, 1); + + fprintf(stderr, "\tmade a cody server\n"); + + int err = 0; + for (;;) + { + server.PrepareToRead (); + while ((err = server.Read ())) + { + if (err == EINTR || err == EAGAIN) + continue; + goto done; + } + + server.ProcessRequests (); + + server.PrepareToWrite (); + while ((err = server.Write ())) + { + if (err == EINTR || err == EAGAIN) + continue; + goto done; + } + } + done:; + if (err > 0) + error ("communication error:%s", xstrerror (err)); + } + + return 0; +} diff --git a/gcc/mapper.h b/gcc/mapper.h new file mode 100644 index 000000000000..f7940126e496 --- /dev/null +++ b/gcc/mapper.h @@ -0,0 +1,117 @@ +/* C++ modules. Experimental! -*- c++ -*- + Copyright (C) 2017-2020 Free Software Foundation, Inc. + Written by Nathan Sidwell while at FaceBook + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +// Mapper interface for client and server bits +#include "cody.hh" +// C++ +#include +#include + +// This is a GCC class, so GCC coding conventions on new bits. +class module_resolver : public Cody::Resolver +{ +public: + using parent = Cody::Resolver; + using module_map = std::map; + +private: + std::string repo; + std::string ident; + module_map map; + int fd_repo = -1; + bool provide_default = true; + +public: + module_resolver (bool def = true); + virtual ~module_resolver () override; + +public: + void set_default (bool d) + { + provide_default = d; + } + void set_ident (char const *i) + { + ident = i; + } + bool set_repo (std::string &&repo, bool force = false); + bool add_mapping (std::string &&module, std::string &&file, + bool force = false); + + // Return +ve line number of error, or -ve errno + int read_tuple_file (int fd, char const *prefix, bool force = false); + int read_tuple_file (int fd, std::string const &prefix, + bool force = false) + { + return read_tuple_file (fd, prefix.empty () ? nullptr : prefix.c_str (), + force); + } + +public: + // Virtual overriders, names are controlle by Cody::Resolver + virtual module_resolver *ConnectRequest (Cody::Server *, unsigned version, + std::string &agent, + std::string &ident) + override; + virtual int ModuleRepoRequest (Cody::Server *) override; + virtual int ModuleExportRequest (Cody::Server *s, std::string &module) + override; + virtual int ModuleImportRequest (Cody::Server *s, std::string &module) + override; + virtual int IncludeTranslateRequest (Cody::Server *s, std::string &include) + override; + +private: + virtual char const *GetCMISuffix () override; + +private: + int cmi_response (Cody::Server *s, std::string &module); +}; + +#ifdef MAPPER_FOR_GCC +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t) (int); +#endif + +class module_client : public Cody::Client +{ + pex_obj *pex = nullptr; + sighandler_t sigpipe = SIG_IGN; + +public: + module_client (Cody::Server *s) + : Client (s) + { + } + module_client (pex_obj *pex, int fd_from, int fd_to); + + module_client (int fd_from, int fd_to) + : Client (fd_from, fd_to) + { + } + +public: + static module_client *open_module_client (location_t loc, const char *option, + void (*set_repo) (const char *), + char const *); + static void close_module_client (location_t loc, module_client *); +}; + +#endif -- 2.47.2