Search code examples
c++gccc++17c++14compiler-warnings

Parameter passing for argument when C++17 is enabled changed to match C++14


I have a minimal reproducible example for the issue here.

It generates the following note on Linux ARM 64-bit GCC 11.2 compiler.

<source>: In function 'void a(Color)':
<source>:19:6: note: parameter passing for argument of type 'Color' when C++17 is enabled changed to match C++14 in GCC 10.1
   19 | void a(Color c) {
      |      ^
Compiler returned: 0

Can someone explain the reason and why you need to:

  1. Inherit a base class
  2. Use defaulted copy constructor.

in order to produce the note.

How worried should I be about this?

Code in the link above for reference.

#include <iostream>

class C {};

// Must inherit to produce the note mentioned below.
class Color : C {
public:
    double x={1.0}, y={2.0}, z={3.0};
    Color() = default;

    // Copy constructor.
    // Default - gives note: parameter passing for argument of type 'rgb_color' when C++17 is enabled changed to match C++14 in GCC 10.1 warning.
    Color(const Color& other) = default;
    
    // Explicit - does NOT give the note
    //Color(const Color& other): C(other),x(other.x),y(other.y),z(other.z){}
};

void a(Color c) {
    std::cout << c.x;
}

int main (){
    Color c{};
    a(c);
}

EDIT: This question goes into the specifics as to the effects of inheritance and copy constructor on the compiler generated note. Please read in detail before marking as duplicate.


Solution

  • The warning message is easy to find by grepping the GCC source code. gcc/aarch64/aarch64.cc says:

    WARN_PSABI_EMPTY_CXX17_BASE
       Indicates that the type includes an artificial empty C++17 base field
       that, prior to GCC 10.1, would prevent the type from being treated as
       a HFA or HVA.  See PR94383 for details.
    

    That's bug 94383: "[8/9/10 Regression] class with empty base passed incorrectly with -std=c++17 on aarch64." A reduced test case is (Godbolt):

    struct EmptyBase {};
    struct Color : EmptyBase {
      float x;
    };
    void a(Color);
    extern Color c;
    void example() {
      a(c);
    }
    

    From Godbolt we can see that in GCC 7.3 through GCC 9.5, the codegen for example was:

      adrp x0, c
      ldr w0, [x0, #:lo12:c]
      b _Z1a5Color
    

    In 6.4-and-earlier, and in 10.2-and-later, it's correctly:

      adrp x0, c
      ldr s0, [x0, #:lo12:c]
      b _Z1a5Color
    

    Meanwhile, Microsoft's Overview of ARM64 ABI Conventions explains those "HFA or HVA" acronyms: An HFA is a Homogeneous Floating-point Aggregate. That is, Color is an aggregate type, and all of its data members are the same floating-point type, so it should be considered an HFA, and so it should be passed in ARM64's floating-point SIMD register s0. But in bug 94383, the empty base class somehow tricked GCC into thinking Color shouldn't be considered an HFA, and so it was passed like any other aggregate in the general-purpose register w0. When GCC 10 fixed this bug, it was considered important enough to add a warning under -Wpsabi about it.

    There is no way to enable/disable this single ABI warning separately from the rest of -Wpsabi; see the code here.

    Removing the empty base class removes the trigger for the GCC bug. Changing the types of the data members to be heterogeneous or non-float makes it a non-HFA, so the issue never comes up. Giving Color any kind of user-provided constructor makes it a non-aggregate, so the issue never comes up.

    Hope this helped!