llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.25k stars 11.67k forks source link

`std::nullptr_t` in aggregates does not follow ABI #41987

Open llvmbot opened 5 years ago

llvmbot commented 5 years ago
Bugzilla Link 42642
Version unspecified
OS All
Reporter LLVM Bugzilla Contributor
CC @dwblaikie,@DougGregor,@zygoloid,@TNorthover

Extended Description

When you create a trivial aggregate containing std::nullptr_t, it does not get passed through RDI/RSI, but gets classified as a memory arg

E.g.:

struct MyVar1 {
    int x = 3;
    std::nullptr_t n = nullptr;
};

void Foo(MyVar1 x);

void CallMyVar1() {
    MyVar1 m;
    Foo(m);  // Sets up stack frame and passes as pointer / MEMORY
}

GCC does this correct.

More elaborate examples: https://gcc.godbolt.org/z/IxLudk

llvmbot commented 5 years ago

I absolutely agree, nullptr / nullptr_t makes perfect sense in the language, the weirdness is in 'what is it then?' for places where a more formal definition is needed on its exact nature, class / non class / layout / bits.

TNorthover commented 5 years ago

C++ defines how it should behave within the language, but the ABI for parameter passing is an entirely separate question. Or would be if we hadn’t sleepwalked into making it a pointer, as Richard points out.

llvmbot commented 5 years ago

std::nullptr_t is weird in many ways unfortunately..... Neither GCC or Clang consider it empty class (or even, a class at all, try struct Foo : std::nullptr_t {} :)

i.e. https://gcc.godbolt.org/z/sVYmpR

ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 5 years ago

It is in non-aggregate already passed as POINTER, not empty struct, GCC and Clang are in agreement on that, e.g. https://gcc.godbolt.org/z/1DsKOt

:( OK. That's grossly wasteful, but being consistent with that makes sense.

llvmbot commented 5 years ago

It is in non-aggregate already passed as POINTER, not empty struct, GCC and Clang are in agreement on that, e.g. https://gcc.godbolt.org/z/1DsKOt

ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 5 years ago

I suppose even without official documentation treating it as a pointer is probably the obvious choice.

I am unconvinced; treating it as an empty struct seems more appropriate, considering it comprises only padding bits. And empty structs aren't passed at all.

TNorthover commented 5 years ago

I suppose even without official documentation treating it as a pointer is probably the obvious choice.

TNorthover commented 5 years ago

GCC does this correct.

Is this something we have to take GCC's word for (i.e. it got there first/is bigger and unilaterally decided the ABI) or is it derivable from ABI documents? I couldn't see any reference in the psABI.