Trying out gcc-12

November 14, 2021

Having updated locally new NixOS system to gcc-11.2.0 I decided I’m familiar enough with NixOS to try yet unreleased gcc-12 on a main system.

Would it just work? That would be boring :)

First I added new gcc snapshot to nixpkgs locally. It was only a matter of pulling in flex as a dependency (gcc does not provide genrated lexers for snapshots, only does fore releases and release candidates):

--- pkgs/development/compilers/gcc/11/default.nix       2021-10-27 07:15:23.996869137 +0100
+++ pkgs/development/compilers/gcc/12/default.nix       2021-11-14 07:34:59.850516073 +0000
@@ -34,6 +34,9 @@
 , gnused ? null
 , cloog # unused; just for compat with gcc4, as we override the parameter on some places
 , buildPackages
+# snapshots don't have all the environment in front of them
+, flex

 # LTO needs libelf and zlib.
@@ -56,8 +59,9 @@
 with lib;
 with builtins;

-let majorVersion = "11";
-    version = "${majorVersion}.2.0";
+let majorVersion = "12";
+    version = "${majorVersion}.0.0";
+    snapDate = "20211107";

     inherit (stdenv) buildPlatform hostPlatform targetPlatform;

@@ -94,8 +102,8 @@
   builder = ../;

   src = fetchurl {
-    url = "mirror://gcc/releases/gcc-${version}/gcc-${version}.tar.xz";
-    sha256 = "sha256-0I7cU2tUw3KhAQ/2YZ3SdMDxYDqkkhK6IPeqLNo2+os=";
+    url = "mirror://gcc/snapshots/${majorVersion}-${snapDate}/gcc-${majorVersion}-${snapDate}.tar.xz";
+    sha256 = "sha256-YrpYs8GHGjpxto8h7JZI3VJLZAn4aRekbCvx+e1WjII=";

   inherit patches;
@@ -161,7 +169,7 @@
     libcCross crossMingw;

   depsBuildBuild = [ ];
-  nativeBuildInputs = [ texinfo which gettext ]
+  nativeBuildInputs = [ flex texinfo which gettext ]
     ++ (optional (perl != null) perl)
     ++ (optional langAda gnatboot)

And a few more lines to set gcc-12 as a default.

I attempted to rebuild packages in my system with nix build -f nixos system.

First failure was a mysterious early ed-1.17 test failure. I updated ed to 1.18-pre3 locally and that fixed the test. I made a mental note to get back to it a bit later to find out what fixed the test.

Then there was a long list of minor failures related to standard library header interdependencies where fix is a one-liner to add required header. A few examples are:

This is the most typical breakage for compiler updates.

Then there were a few failures related to now forbidden std::string(NULL) constructor. The fix is to use simpler std::string() constructor. A few affected projects are:

Then I noticed another change in ADL resolution, where operator<< was not searched in current namespace if none of arguments are in current namespace. clang had this behaviour for a while. Now gcc follows the lead. Only one project is affected so far:

After getting through this trivia I got an unusual build failure for libX11. Here is a shorter example:

#include <string.h>
int extract(char*);
int XReadBitmapFileData (void) {
    char name_and_type[255];
    for (;;) {
        extract (name_and_type);
        char * type = strrchr (name_and_type, '_');
        if (type) type++; else type = name_and_type;
        if (strcmp ("hot", type) == 0) {
        if (type-- == name_and_type || type-- == name_and_type) continue;
        if (strcmp ("ax_hot", type) == 0) return 1;
  return -1;

It took me a while to wrap my head around what is being done here. And after that while I still got it wrong :) (but not by too much).

Here is what gcc-12 has to say about the code:

# ok:
$ gcc-11.2.0 -Wall -Werror=array-bounds -fno-strict-aliasing -O2 -c bug.c -o bug.o
# bad:
$ gcc-12.0.0 -Wall -Werror=array-bounds -fno-strict-aliasing -O2 -c bug.c -o bug.o
bug.c: In function 'XReadBitmapFileData':
bug.c:10:48: error: array subscript -2 is outside array bounds of 'char[9223372036854775807]' [-Werror=array-bounds]
   10 |             if (type-- == name_and_type || type-- == name_and_type) continue;
      |                                            ~~~~^~
bug.c:4:14: note: at offset [0, 253] into object 'name_and_type' of size 255
    4 |         char name_and_type[255];
      |              ^~~~~~~~~~~~~
cc1: some warnings being treated as errors

Note: it’s normally just a warning. But libX11 explicitly uses “-Werror=array-bounds” as part of it’s build system assuming this warning can catch important problems.

The error makes no sense in tis case: a) there is no array dereference, b) there should be no out-of-bounds pointers here.

I was also not sure if (type– == name_and_type || type– == name_and_type) is a valid construct. I filed There Andrew explained that || is a sequence point and it has expected defined behaviour. In hindsight it’s obvious as || is also a lazy evaluation device.

Apparently this build failure is somewhat common. Same failure on identical code is present in:

Having sorted the above I returned to ed-1.17 failure. I assumed it was some ancient undefined behaviour in ed uncovered by new gcc optimisations (like recent modref improvements). I looked at the diff between ed-1.17 and ed-1.18-pre3 tarballs but found nothing suspicious.

ed is an old and short codebase. Instead of staring a lot at generated assembly I decided to shrink it down to something I could easily understand and possibly fix. In the hindsight it was a good decision.

After I arrived to a 2KB self-contained example of C code I still could not see what exactly gcc-12 does to it to make it fail.

I filed and Aldy instantly fixed very obscure PHI importing order bug. From what I understand we used stale variable names before a “rename” and used incorrect metadata attached to them (like value ranges).

These kinds of bugs are very hard to extract from large projects. Even for ed case it took me 3 evenings. On a positive side now I know ed’s editor model :)

After getting ed fixed I encountered an (already fixed) ICE on gperftools:

Currently I can build almost all packages for my system with gcc-12. I have a few local hacks too dirty to share, but they are mostly about getting headers imported correctly and getting around too aggressive warnings-as-errors in a few packages libe libjxl.

The only yet unsolved mystery is 4 newly failing tests on llvm-13 package. Somehow llvm generates FunctionLength=0 unwind information for learly non-empty functions. I suspect (hope) it’s another gcc bug. I’m trying to debug it right now. UPDATE: confirmed and extracted example to illustrate gcc bug

Fun facts

I think gcc-12 is quite usable for early experiments.

Have fun!