diff mbox series

C++ runtime library selection via -stdlib=libstdc++/libc++ (WIP)

Message ID 1679311.OiYO2A93eY@portia.local
State New
Headers show
Series C++ runtime library selection via -stdlib=libstdc++/libc++ (WIP) | expand

Commit Message

René J.V. Bertin Nov. 9, 2017, 5:17 p.m. UTC
Hi,

A while back I filed a feature request for a build option to use the libc++ runtime, with a mostly PoC patch attached.
Such a feature would increase GCC's usability on platforms like Apple's OS for Mac, but possibly on FreeBSD too. (And if it's technically possible to use Microsoft's runtime that could become another option down the road.)

I've since had the time to test this feature more thoroughly, and have begun bringing it into a form where it might be considerable for inclusion. I'm attaching a first draft, including only the changes to the driver but none of the required changes to the build system yet. I'm doing this in my local 7.2.0 build, so the patch is against that source, for now. I'm hoping the modified files evolve little enough that the patch also applies to the current development branch, and that it's thus useful enough to get some constructive feedback telling me I'm on the right path here.

I'd also really like to rally some "coworkers" on this, esp. if they're familiar with the (rather daunting) configure-and-build system and the procedure of "getting a new feature in"!

About the attached patch and approach:

I've copied the clang option syntax for selecting the C++ runtime: `-stdlib=libstdc++` or `-stdlib=libc++`, this is to maximise compiler compatibility. I think the compiler should be configured to use the platform runtime by default, i.e. libc++ on Mac OS X 10.9 and later. Currently this is hardwired with #ifdefs but that should probably become a configure setting.

The C++ runtime headerfiles for libc++ will have to found in the appropriate location, which will need to be configurable as on Mac they typically live inside the Xcode app bundle which is freely relocatable.

There is a catch with using libc++: older versions don't contain the sized C++14 new/delete operators (those were provided implicitly by clang). The operators are there in the libc++ from LLVM 3.9 and newer, but it appears that means that the system libc++ only contains them in Mac OS 10.12 and newer. It is of course possible to install a newer version, link to it and run it via DYLD_INSERT_LIBRARIES . The other option that appears to work is to use libsupc++ as the provider for those operators by including it as the last link library, in line with the procedure on llvm.org how to use libc++ on Linux. My patch currently adds libsupc++ on 10.11 and earlier; this should also be done more properly (though I presume libsupc++ will only be used if it provides symbols not already provided by the libraries linked before it).

Thanks,
René Bertin

Comments

Joseph Myers Nov. 9, 2017, 5:41 p.m. UTC | #1
Your __APPLE__ conditional would appear to condition something on whether 
the *host* is an Apple system.  If you wish to change the default for 
Apple systems, presumably that should be a matter of whether the *target* 
is an Apple system.  (In general we want to avoid host conditionals as 
much as possible, and if needed, limit them to places such as system.h if 
possible, and handle target conditionals via target hooks not target 
macros.)

All options should be accessible by names starting with "--", meaning the 
new options should have such aliases added as well.
René J.V. Bertin Nov. 9, 2017, 7:10 p.m. UTC | #2
On Thursday November 09 2017 17:41:39 Joseph Myers wrote:

>Your __APPLE__ conditional would appear to condition something on whether 
>the *host* is an Apple system.  If you wish to change the default for 

Evidently. As I said, there are a number of settings that will (probably) need to be determined at the configure stage.
I must admit I hadn't thought about cross-compiling (or cross-building) at all. My first concern has been to get the feature working so I can test it.
(I, and potentially other Mac users; my patch is currently set up to integrate with the MacPorts package for gcc7; a bit of wide-spread testing in the wild cannot be a bad thing before incorporating this, right? :))

I wouldn't particularly mind in fact if making libc++ the default runtime library were made through an explicit configure option. That might even be better for users of libc++-based systems who have integrated g++ and its incompatible runtime into their workflows. I expect the powers that be on here will have a better educated opinion on this.

>All options should be accessible by names starting with "--", meaning the 
>new options should have such aliases added as well.

Ok, I'll have a look how that would work, I was expecting that the check for "this option with an extra dash" would be done in code.

I hope I'm not violating any rules in processing the same options in 2 locations (once to know where to look for C++ headers, once to know which runtime lib to link)?

R.
diff mbox series

Patch

diff --git gcc/incpath.c
--- gcc/orig.incpath.c	2017-02-20 12:38:16.000000000 +0100
+++ gcc/incpath.c	2017-05-11 19:02:37.000000000 +0200
@@ -129,6 +129,10 @@ 
   int relocated = cpp_relocated ();
   size_t len;
 
+  if (cxx_stdinc == 2)
+    {
+      add_path (xstrdup ("@LLVMHEADERPATH@"), SYSTEM, true, false);
+    }
   if (iprefix && (len = cpp_GCC_INCLUDE_DIR_len) != 0)
     {
       /* Look for directories that start with the standard prefix.
@@ -136,7 +140,7 @@ 
 	 IPREFIX and search them first.  */
       for (p = cpp_include_defaults; p->fname; p++)
 	{
-	  if (!p->cplusplus || cxx_stdinc)
+	  if (!p->cplusplus || cxx_stdinc == 1)
 	    {
 	      /* Should we be translating sysrooted dirs too?  Assume
 		 that iprefix and sysroot are mutually exclusive, for
@@ -167,7 +171,7 @@ 
 
   for (p = cpp_include_defaults; p->fname; p++)
     {
-      if (!p->cplusplus || cxx_stdinc)
+      if (!p->cplusplus || cxx_stdinc == 1)
 	{
 	  char *str;
 
@@ -474,6 +478,9 @@ 
   /* CPATH and language-dependent environment variables may add to the
      include chain.  */
   add_env_var_paths ("CPATH", BRACKET);
+  /* cxx_stdinc is no longer a bool but also indicates which C++ headers to include */
+  if (stdinc && cxx_stdinc == 2)
+    add_env_var_paths ("LIBCPP_INCLUDE_PATH", SYSTEM);
   add_env_var_paths (lang_env_vars[idx], SYSTEM);
 
   target_c_incpath.extra_pre_includes (sysroot, iprefix, stdinc);
diff --git gcc/cp/g++spec.c
--- gcc/cp/orig.g++spec.c	2017-09-07 22:30:27.000000000 +0200
+++ gcc/cp/g++spec.c	2017-10-31 20:44:58.000000000 +0100
@@ -23,6 +23,10 @@ 
 #include "tm.h"
 #include "opts.h"
 
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#endif
+
 /* This bit is set if we saw a `-xfoo' language specification.  */
 #define LANGSPEC	(1<<1)
 /* This bit is set if they did `-lm' or `-lmath'.  */
@@ -48,9 +52,18 @@ 
 #ifndef LIBSTDCXX
 #define LIBSTDCXX "stdc++"
 #endif
+/* libc++ support: */
+#ifndef LIBCXX
+#define LIBCXX "c++"
+#define LIBCXXABI "c++abi"
+/* using libsupc++ is a hack, probably to be dropped on systems that have a new enough libc++
+   which has `operator delete(void*, unsigned long)` (introduced in libcxx rev. 229281) */
+#define LIBSUPCXX "supc++"
+#endif
 #ifndef LIBSTDCXX_PROFILE
 #define LIBSTDCXX_PROFILE LIBSTDCXX
 #endif
+
 #ifndef LIBSTDCXX_STATIC
 #define LIBSTDCXX_STATIC NULL
 #endif
@@ -65,7 +78,14 @@ 
   /* If nonzero, the user gave us the `-p' or `-pg' flag.  */
   int saw_profile_flag = 0;
 
-  /* What do with libstdc++:
+#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+	/* TODO the configure script should determine whether the targetted host uses libc++ */
+  int use_libcxx = 1;
+#else
+  int use_libcxx = 0;
+#endif
+
+  /* What do with libstdc++ (or libc++):
      -1 means we should not link in libstdc++
      0  means we should link in libstdc++ if it is needed
      1  means libstdc++ is needed and should be linked in.
@@ -141,6 +161,14 @@ 
 	  library = -1;
 	  break;
 
+	case OPT_stdlib_libc__:
+	  /* use libc++ */
+	  use_libcxx = 1;
+	  break;
+	case OPT_stdlib_libstdc__:
+	  use_libcxx = 0;
+	  break;
+
 	case OPT_l:
 	  if (strcmp (arg, MATH_LIBRARY) == 0)
 	    {
@@ -331,7 +359,7 @@ 
       j++;
     }
 
-  /* Add `-lstdc++' if we haven't already done so.  */
+  /* Add `-lstdc++' or -lc++ if we haven't already done so.  */
   if (library > 0)
     {
 #ifdef HAVE_LD_STATIC_DYNAMIC
@@ -342,19 +370,44 @@ 
 	  j++;
 	}
 #endif
-      generate_option (OPT_l,
-		       saw_profile_flag ? LIBSTDCXX_PROFILE : LIBSTDCXX, 1,
-		       CL_DRIVER, &new_decoded_options[j]);
-      added_libraries++;
-      j++;
-      /* Add target-dependent static library, if necessary.  */
-      if ((static_link || library > 1) && LIBSTDCXX_STATIC != NULL)
-	{
-	  generate_option (OPT_l, LIBSTDCXX_STATIC, 1,
-			   CL_DRIVER, &new_decoded_options[j]);
-	  added_libraries++;
-	  j++;
-	}
+		if (use_libcxx)
+			{
+				generate_option (OPT_l, LIBCXX, 1,
+					  CL_DRIVER, &new_decoded_options[j]);
+				added_libraries++;
+				j++;
+				/* add -lc++abi */
+				generate_option (OPT_l, LIBCXXABI, 1,
+					  CL_DRIVER, &new_decoded_options[j]);
+				added_libraries++;
+				j++;
+#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (!defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12)
+				/* the system libc++ doesn't have the C++14 sized new/delete operators;
+					 get them from libsup++. This is a bit of a hack but libc++ supports
+					 it.
+					 TODO: use a proper configure-time check to determine if this is required. */
+				generate_option (OPT_l, LIBSUPCXX, 1,
+					  CL_DRIVER, &new_decoded_options[j]);
+				added_libraries++;
+				j++;
+#endif
+			}
+		else
+			{
+				generate_option (OPT_l,
+					  saw_profile_flag ? LIBSTDCXX_PROFILE : LIBSTDCXX, 1,
+					  CL_DRIVER, &new_decoded_options[j]);
+				added_libraries++;
+				j++;
+				 /* Add target-dependent static library, if necessary.  */
+				 if ((static_link || library > 1) && (LIBSTDCXX_STATIC != NULL))
+				{
+				  generate_option (OPT_l, LIBSTDCXX_STATIC, 1,
+						   CL_DRIVER, &new_decoded_options[j]);
+				  added_libraries++;
+				  j++;
+				}
+			}
 #ifdef HAVE_LD_STATIC_DYNAMIC
       if (library > 1 && !static_link)
 	{
diff --git gcc/c-family/c.opt
--- gcc/c-family/orig.c.opt	2017-03-31 16:24:27.000000000 +0200
+++ gcc/c-family/c.opt	2017-10-31 11:31:59.000000000 +0100
@@ -1992,6 +1992,15 @@ 
 C ObjC Alias(std=c11)
 Conform to the ISO 2011 C standard.
 
+stdlib=libc++
+C++ ObjC++
+Specify the C++ standard library to use: LLVM libc++
+(default on Mac OS X 10.9 and up)
+
+stdlib=libstdc++
+C++ ObjC++
+Specify the C++ standard library to use: GNU libstdc++
+
 traditional
 Driver
 
diff --git gcc/c-family/c-opts.c
--- gcc/c-family/orig.c-opts.c	2017-01-05 15:17:07.000000000 +0100
+++ gcc/c-family/c-opts.c	2017-10-31 20:45:16.000000000 +0100
@@ -41,6 +41,10 @@ 
 #include "mkdeps.h"
 #include "dumpfile.h"
 
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#endif
+
 #ifndef DOLLARS_IN_IDENTIFIERS
 # define DOLLARS_IN_IDENTIFIERS true
 #endif
@@ -87,9 +91,17 @@ 
 /* Zero disables all standard directories for headers.  */
 static bool std_inc = true;
 
-/* Zero disables the C++-specific standard directories for headers.  */
+/* Zero disables the C++-specific standard directories for headers */
 static bool std_cxx_inc = true;
 
+/* 1 enables the libstdc++ specific standard header directories;
+   2 enables the libc++ specific standard header directories. */
+#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+static int std_cxx_header_type = 2;
+#else
+static int std_cxx_header_type = 1;
+#endif
+
 /* If the quote chain has been split by -I-.  */
 static bool quote_chain_split;
 
@@ -597,7 +609,21 @@ 
       break;
 
     case OPT_nostdinc__:
-      std_cxx_inc = false;
+      std_cxx_inc = 0;
+      break;
+
+    /* OPT_stdlib_libc__ & OPT_stdlib_libstdc__ are also handled in g++spec.c */
+    case OPT_stdlib_libc__:
+      /* use libc++ */
+      warning (OPT_stdlib_libc__,
+        "using the LLVM/libc++ standard C++ headers");
+      std_cxx_header_type = 2;
+      break;
+
+    case OPT_stdlib_libstdc__:
+      warning (OPT_stdlib_libstdc__,
+        "using the GNU/libstdc++ standard C++ headers");
+      std_cxx_header_type = 1;
       break;
 
     case OPT_o:
@@ -762,7 +788,7 @@ 
   sanitize_cpp_opts ();
 
   register_include_chains (parse_in, sysroot, iprefix, imultilib,
-			   std_inc, std_cxx_inc && c_dialect_cxx (), verbose);
+			   std_inc, std_cxx_inc && c_dialect_cxx () ? std_cxx_header_type : 0, verbose);
 
 #ifdef C_COMMON_OVERRIDE_OPTIONS
   /* Some machines may reject certain combinations of C