]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
adding libcody to lto-wrapper, fork_execute serially
authorJohn Ravi <jjravi@ncsu.edu>
Wed, 1 Jul 2020 01:33:25 +0000 (21:33 -0400)
committerJohn Ravi <jjravi@ncsu.edu>
Wed, 1 Jul 2020 01:33:25 +0000 (21:33 -0400)
14 files changed:
Makefile.in
Makefile.tpl
configure
configure.ac
gcc/Makefile.in
gcc/config.in
gcc/configure
gcc/configure.ac
gcc/cp/Make-lang.in
gcc/lto-wrapper.c
gcc/mapper-client.cc [new file with mode: 0644]
gcc/mapper-resolver.cc [new file with mode: 0644]
gcc/mapper-server.cc [new file with mode: 0644]
gcc/mapper.h [new file with mode: 0644]

index 36e369df6e7e2fb01e3e2adcf863faa08cc15ce4..345ef1b76e2c682f3e47215f65f53346c8844746 100644 (file)
@@ -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@
index efed1511750412f7dff03a363779b78c6ecccb41..6b1a4d723028cbe0aaed44b38a00dc90541d6fee 100644 (file)
@@ -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@
index a0c5aca9e8d5cae2782c8fe4625a501853dc226a..fc2666567335f443e6f64a61dde554672cfda50b 100755 (executable)
--- 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.
index 1a53ed418e4d97606356b14a17b50186c79adcd3..c1a1f2f9305ea441e3bc3fa9925fd619bd1c54b1 100644 (file)
@@ -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])],
index d5dcc03d59be696d535d0df21de8f63ab58fb894..0b2543aed7519e59e2609c4a12c7addb2d030623 100644 (file)
@@ -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.
index 364eba4773744f2a5b415dbccc01242df1fc289f..d297dbc09d46f36e05836803f68195762868c055 100644 (file)
 #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
 #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 <ext/hash_map> header file. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_EXT_HASH_MAP
 #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
 #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
 #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
 #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
 #endif
 
 
+/* Define if <sys/signal.h> 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
index f224679ed3ee5e4bbd80318a3eb2f32561dfd89c..6a69799836d6b186c5a6ebe00071fecdbc32e7af 100755 (executable)
@@ -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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+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 <sys/epoll.h>
+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 <sys/select.h>
+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 <sys/select.h>
+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 <sys/socket.h>
+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 <arpa/inet.h>
+#include <netinet/in.h>
+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"
index e83f0833ef3fb19d6f2d68cc3c24e039da997a08..81b6cb70d2c06c9191bbbc4a491e78eb592925f4 100644 (file)
@@ -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 <sys/signal.h> 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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>]],[[
+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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>]],[[
+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 <sys/epoll.h>]],[[
+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 <sys/select.h>]],[[
+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 <sys/select.h>]],[[
+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 <sys/socket.h>]],[[
+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 <arpa/inet.h>
+#include <netinet/in.h>]],[[
+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)
index 7896591dd4b2b995b05bc89e807af57680dc4a24..41de295ef9ccad9847eb8369e5a866d0b39af5e0 100644 (file)
@@ -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++
index 939a83ac73a4e8238395b60f0bcdca88c78a8aeb..861132e92a028c34a2f82a08106cb812a2cbca77 100644 (file)
@@ -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 <stdio.h>
+#include <unistd.h>
+
+#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<const char*> 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 (file)
index 0000000..6e7093f
--- /dev/null
@@ -0,0 +1,302 @@
+/* C++ modules.  Experimental!
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> 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
+<http://www.gnu.org/licenses/>.  */
+
+#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 <char *> (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 '<':
+             // <from>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 (file)
index 0000000..4a84d64
--- /dev/null
@@ -0,0 +1,271 @@
+/* C++ modules.  Experimental! -*- c++-mode -*-
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> 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
+<http://www.gnu.org/licenses/>.  */
+
+#include "mapper.h"
+// C++
+#include <algorithm>
+// C
+#include <cstring>
+// OS
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#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 <char const *> (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<std::string> &args) {
+  char **new_argv = (char **)malloc((args.size()) * sizeof(char *));
+  new_argv[args.size()-1] = NULL;
+
+  int i = 0;
+  for (std::vector<std::string>::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 (file)
index 0000000..82d1835
--- /dev/null
@@ -0,0 +1,990 @@
+/* C++ modules.  Experimental!
+   Copyright (C) 2018-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> 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
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "mapper.h"
+
+// C++
+#include <set>
+#include <vector>
+// 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 <sys/socket.h>
+# ifdef HAVE_AF_UNIX
+/* sockaddr_un  */
+#  include <sys/un.h>
+# endif
+# include <netinet/in.h>
+# ifdef HAVE_AF_INET6
+/* sockaddr_in6, getaddrinfo, freeaddrinfo, gai_sterror, ntohs, htons.  */
+#  include <netdb.h>
+# endif
+#ifdef HAVE_INET_NTOP
+/* inet_ntop.  */
+#include <arpa/inet.h>
+#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 <getopt.h>
+
+// Select or epoll
+#ifdef NETWORKING
+#ifdef HAVE_EPOLL
+/* epoll_create, epoll_ctl, epoll_pwait  */
+#include <sys/epoll.h>
+#endif
+#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
+/* pselect or select  */
+#include <sys/select.h>
+#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, netmask_cmp> netmask_set_t;
+typedef std::vector<netmask> 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<Cody::Server *> 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 (file)
index 0000000..306ee5c
--- /dev/null
@@ -0,0 +1,120 @@
+/* C++ modules.  Experimental! -*- c++ -*-
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> 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
+<http://www.gnu.org/licenses/>.  */
+
+// Mapper interface for client and server bits
+#include "cody.hh"
+// C++
+#include <string>
+#include <map>
+
+// 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<std::string, std::string>;
+
+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<std::string> &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