From: John Ravi Date: Wed, 1 Jul 2020 01:33:25 +0000 (-0400) Subject: adding libcody to lto-wrapper, fork_execute serially X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c647b271e82e751c2be182f498b69b30d068bf76;p=thirdparty%2Fgcc.git adding libcody to lto-wrapper, fork_execute serially --- 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..0b2543aed751 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,12 +2197,28 @@ 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) + +MAPPER_SERVER_OBJS := mapper-server.o mapper-resolver.o collect-utils.o +mapper-server$(exeext): $(MAPPER_SERVER_OBJS) libcommon-target.a $(LIBDEPS) + +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ + $(MAPPER_SERVER_OBJS) libcommon-target.a version.o $(CODYLIB) $(CPPLIB) $(LIBINTL) $(LIBICONV) $(LIBBACKTRACE) $(LIBIBERTY) $(LIBDECNUMBER) $(HOST_LIBS) + + +#MAPPER_SERVER_OBJS := mapper-server.o mapper-resolver.o collect-utils.o +#mapper-server$(exeext): $(MAPPER_SERVER_OBJS) libcommon-target.a $(LIBDEPS) +# +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ +# $(MAPPER_SERVER_OBJS) libcommon-target.a version.o $(CODYLIB) $(LIBS) + # Files used by all variants of C or by the stand-alone pre-processor. CFLAGS-c-family/c-opts.o += @TARGET_SYSTEM_ROOT_DEFINE@ @@ -3683,6 +3709,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..861132e92a02 100644 --- a/gcc/lto-wrapper.c +++ b/gcc/lto-wrapper.c @@ -48,6 +48,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,6 +89,45 @@ static bool xassembler_options_error = false; const char tool_name[] = "lto-wrapper"; +/********************************************************************/ + +// 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. */ + +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 @@ -1818,6 +1862,7 @@ cont: qsort (ltrans_priorities, nr, sizeof (int) * 2, cmp_priority); } + auto *mapper = get_mapper (main_source_loc); /* Execute the LTRANS stage for each input file (or prepare a makefile to invoke this in parallel). */ for (i = 0; i < nr; ++i) @@ -1861,8 +1906,31 @@ cont: } else { - fork_execute (new_argv[0], CONST_CAST (char **, new_argv), - true); + //fprintf(stderr, "\tRAVI PRINT: %s ", new_argv[0]); + mapper->Cork(); + + // TODO: better way to figure out the number of arguments? + for (j = 1; new_argv[j] != NULL; ++j); + fprintf(stderr, "argc: %d\n", j); + + //std::vector v_new_argv(new_argv, new_argv + j); + //mapper->LTOCompile(v_new_argv); + mapper->LTOCompile(new_argv, j); + 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); } diff --git a/gcc/mapper-client.cc b/gcc/mapper-client.cc new file mode 100644 index 000000000000..6e7093f91337 --- /dev/null +++ b/gcc/mapper-client.cc @@ -0,0 +1,302 @@ +/* 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) +{ + module_client *c = nullptr; + std::string ident; + std::string name; + char const *errmsg = nullptr; + + if (o && o[0]) + { + /* 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) + { + // Make a default in-process client + auto r = new module_resolver (); + auto *s = new Cody::Server (r); + c = new module_client (s); + 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 + if (!c->IsDirect ()) + /* We need to ignore sig pipe for a while. */ + c->sigpipe = signal (SIGPIPE, SIG_IGN); +#endif + + if (errmsg) + error_at (loc, "failed %s mapper %qs", errmsg, name.c_str ()); + + // now wave hello! + c->Cork (); + c->Connect (std::string ("GCC"), ident); + //c->ModuleRepo (); + auto packets = c->Uncork (); + + auto &connect = packets[0]; + if (connect.GetCode () == Cody::Client::PC_CONNECT) + ; + else if (connect.GetCode () == Cody::Client::PC_ERROR) + 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..4a84d647b875 --- /dev/null +++ b/gcc/mapper-resolver.cc @@ -0,0 +1,271 @@ +/* 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 + +#include "collect-utils.h" + +module_resolver::module_resolver (bool def) + : provide_default (def) +{ +} + +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; +} + +int module_resolver::LTOCompileRequest (Cody::Server *s, std::vector &args) { + char **new_argv = (char **)malloc((args.size()) * sizeof(char *)); + new_argv[args.size()-1] = NULL; + + int i = 0; + for (std::vector::iterator arg = args.begin() ; arg != args.end(); ++arg, ++i) { + + // ignore "LTO-COMPILE" + if(i == 0) continue; + //new_argv[i++] = (char *)malloc(args[i].length() * sizeof(char)); + + // TODO: type safety + new_argv[i-1] = (char *)(*arg).c_str(); + //fprintf(stderr, "*arg[?] = %s\n", (*arg).c_str()); + //fprintf(stderr, "*new_argv[%d] = %s\n", i-1, new_argv[i-1]); + } + + //fprintf(stderr, "\tcalling fork_execute\n"); + fork_execute (new_argv[0], new_argv, true); + + // TODO: send back a compile status response + //s->LTOResponse(); + return 0; +} + diff --git a/gcc/mapper-server.cc b/gcc/mapper-server.cc new file mode 100644 index 000000000000..82d1835763b6 --- /dev/null +++ b/gcc/mapper-server.cc @@ -0,0 +1,990 @@ +/* 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; +} + +/* TODO: Delete tempfiles. Called from utils_cleanup. */ + +void tool_cleanup (bool) { + +} + +// TODO: undefined reference? +unsigned int input_location=0; +void fatal_error (unsigned int f, const char *str, ...) { + +} + +/* 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; + 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[]) +{ + 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); + +#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; + if (flag_root) + r.set_repo (flag_root); + + std::string name; + int sock_fd = -1; /* Socket fd, otherwise stdin/stdout. */ + + if (argno != argc) + { + name = argv[argno]; + sock_fd = maybe_parse_socket (name, &r); + if (!name.empty ()) + argno++; + } + + 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); + +#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) + { + server (name[0] != '=', sock_fd, &r); + if (name[0] == '=') + unlink (name.c_str () + 1); + } + else +#endif + { + gcc_assert (sock_fd < 0); + auto server = Cody::Server (&r, 0, 1); + + 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..306ee5cccb60 --- /dev/null +++ b/gcc/mapper.h @@ -0,0 +1,120 @@ +/* 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; + + virtual int LTOCompileRequest (Cody::Server *s, std::vector &args) + 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