Porting to GCC 9

The GCC 9 release series differs from previous GCC releases in a number of ways. Some of these are a result of bug fixing, and some old behaviors have been intentionally changed to support new standards, or relaxed in standards-conforming ways to facilitate compilation or run-time performance.

Some of these changes are user visible and can cause grief when porting to GCC 9. This document is an effort to identify common issues and provide solutions. Let us know if you have suggestions for improvements!

C language issues

Block scope compound literal's lifetime

The C standard says that compound literals which occur inside of the body of a function have automatic storage duration associated with the enclosing block. Older GCC releases were putting such compound literals into the scope of the whole function, so their lifetime actually ended at the end of containing function. This has been fixed in GCC 9. Code that relied on this extended lifetime needs to be fixed, move the compound literals to whatever scope they need to accessible in.


      struct S { int a, b; };
      int foo(void) {
        // The following line no longer compiles
        struct S *p = &({ (struct S) { 1, 2 }; });
        struct S *q;
        {
          q = &(struct S) { 3, 4 };
        }
        // This is invalid use after lifetime of the compound literal
        // ended.
        return q->b;
      }
  

OpenMP data sharing

GCC releases before 9 were implementing an OpenMP 3.1 data sharing rule that const qualified variables without mutable member are predetermined shared, but as an exception may be specified in the firstprivate clause. OpenMP 4.0 dropped this rule, but in the hope that the incompatible change will be reverted GCC kept implementing the previous behavior. Now that for OpenMP 5.0 it has been confirmed this is not going to change, GCC 9 started implementing the OpenMP 4.0 and later behavior. When not using default clause or when using default(shared), this makes no difference, but if using default(none), previously the choice was not specify the const qualified variables on the construct at all, or specify in firstprivate clause. In GCC 9 as well as for OpenMP 4.0 compliance, those variables need to be specified on constructs in which they are used, either in shared or in firstprivate clause. Specifying them in firstprivate clause is one way to achieve compatibility with both older GCC versions and GCC 9, another option is to drop the default(none) clause. In C++, const variables with constant initializers which are not odr-used in the region, but replaced with their constant initializer are not considered to be referenced in the region for default(none) purposes.


      int get (void);
      void use (int);
      void foo (void) {
        const int a = get ();
        const int b = 1;
        #pragma omp parallel for default(none)
        for (int i = 0; i < a; i += b)
          ;
        // The above used to compile with GCC 8 and older, but will
        // not anymore with GCC 9.  firstprivate(a, b) clause needs
        // to be added for C, for C++ it could be just firstprivate(a)
        // to make it compatible with all GCC releases.
      }
      const int huge_array[1024] = { ... };
      void bar (void) {
        #pragma omp parallel for default(none)
        for (int i = 0; i < 1024; i++)
          use (huge_array[i]);
        // Similarly, this used to compile with GCC 8 and older and
        // will not anymore.  Adding firstprivate(huge_array) is
        // probably undesirable here, so, either
        // default(none) shared(huge_array) should be used and it will
        // only support GCC 9 and later, or default(none) should be
        // removed and then it will be compatible with all GCC releases
        // and huge_array will be shared.
      }
  

C++ language issues

operator new(size_t, nothrow_t) calls operator new(size_t)

GCC 9 implements the requirement introduced in the C++ 2011 standard that the nothrow version of operator new calls the ordinary, throwing version of operator new (and catches any exception and instead returns a null pointer). This was changed by DR 206 to ensure that the various forms of operator new do not become decoupled if a user only replaces the ordinary operator new.

Code that only replaces one version of operator new and expects the other versions to be unaffected might change behaviour when using GCC 9.

If your program uses a replacement operator new(size_t, nothrow_t) then it must also replace operator new(size_t) and operator delete(void*), and ensure memory obtained from the nothrow version of new can be freed by the ordinary version of operator delete.

The simplest solution is to only replace the ordinary operator new(size_t) and operator delete(void*) and operator delete(void*, size_t) functions, and the replaced versions will be used by all of operator new(size_t, nothrow_t), operator new[](size_t) and operator new[](size_t, nothrow_t) and the corresponding operator delete functions. To support types with extended alignment you may also need to replace operator new(size_t, align_val_t) and operator delete(void*, align_val_t) and operator delete(void*, align_val_t) (which will then be used by the nothrow and array forms for extended alignments).