Bug 11740 – [64-bit] Struct with constructor incorrectly passed on stack to extern(C++) function
Status
RESOLVED
Resolution
FIXED
Severity
critical
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
All
Creation time
2013-12-14T09:22:00Z
Last change time
2014-07-23T16:00:13Z
Keywords
wrong-code
Assigned to
nobody
Creator
yebblies
Comments
Comment #0 by yebblies — 2013-12-14T09:22:12Z
In func, 's' is passed on the stack instead of in a register pair, only when the constructor is present. This blocks DDMD on linux64.
extern(C++)
class C
{
extern(C++) static void func(S s, C c)
{
assert(c);
assert(s.filename);
assert(s.linnum);
}
}
struct S
{
const(char)* filename;
uint linnum;
this(const(char)* filename, uint linnum) {}
}
int main()
{
C.func(S(null, 0), null);
return 0;
}
0000000000000000 <_ZN1C4funcE1SPS_>:
0: 55 push %rbp
1: 48 8b ec mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 48 89 7d f8 mov %rdi,-0x8(%rbp) (should be in rdx, not rdi)
c: 48 83 7d f8 00 cmpq $0x0,-0x8(%rbp)
11: 75 0a jne 1d <_ZN1C4funcE1SPS_+0x1d>
13: bf 07 00 00 00 mov $0x7,%edi
18: e8 00 00 00 00 callq 1d <_ZN1C4funcE1SPS_+0x1d>
19: R_X86_64_PC32 _D4test8__assertFiZv-0x4
1d: 48 83 7d 10 00 cmpq $0x0,0x10(%rbp)
22: 75 0a jne 2e <_ZN1C4funcE1SPS_+0x2e>
24: bf 08 00 00 00 mov $0x8,%edi
29: e8 00 00 00 00 callq 2e <_ZN1C4funcE1SPS_+0x2e>
2a: R_X86_64_PC32 _D4test8__assertFiZv-0x4
2e: 83 7d 18 00 cmpl $0x0,0x18(%rbp)
32: 75 0a jne 3e <_ZN1C4funcE1SPS_+0x3e>
34: bf 09 00 00 00 mov $0x9,%edi
39: e8 00 00 00 00 callq 3e <_ZN1C4funcE1SPS_+0x3e>
3a: R_X86_64_PC32 _D4test8__assertFiZv-0x4
3e: c9 leaveq
3f: c3 retq
It works correctly if StructDeclaration::isPOD is changed to not reject all structs with constructors, but there is a comment warning against that.
Comment #1 by ibuclaw — 2013-12-15T13:48:44Z
Errm...
This is working correctly as designed. Structs with constructors are considered non-POD, so they cannot be bit-copied in and out of registers.
Comment #2 by ibuclaw — 2013-12-15T14:14:12Z
(In reply to comment #1)
> This is working correctly as designed. Structs with constructors are considered
> non-POD, so they cannot be bit-copied in and out of registers.
Maybe we should start considering whether structs are trivially copyable like C++ does.
eg:
StructDeclaration::isTriviallyCopyable()
if has no copy constructor
&& if has no postblit
&& if has a trivial destructor
&& if not nested inside another struct/class.
Which slightly differs from isPOD.
To compare behaviours, GDC should return from S ctor in memory, then pass via registers to C.func. Call it a bug in GDC, but we only listen to isPOD for returning structs, not passing them. ;)
Comment #3 by yebblies — 2013-12-15T20:52:29Z
(In reply to comment #1)
> Errm...
>
> This is working correctly as designed. Structs with constructors are considered
> non-POD, so they cannot be bit-copied in and out of registers.
No, it is not. Constructors should not cause a struct to be considered non-POD for extern(C++) functions. I don't care what we do with extern(D) functions, but we need to match the C++ compiler.
"If a C++ object has either a non-trivial copy constructor or a non-trivial destructor [11], it is passed by invisible reference (the object is replaced in the parameter list by a pointer that has class INTEGER)[12]"
(In reply to comment #5)
> extern(C++) functions must do what the corresponding c++ compiler does.
The definition of POD:
A PODS type in C++ is defined as either a scalar type or a PODS class. A PODS class has no user-defined copy assignment operator, no user-defined destructor, and no non-static data members that are not themselves PODS. Moreover, a PODS class must be an aggregate, meaning it has no user-declared constructors, no private nor protected non-static data, no base classes and no virtual functions.
So if g++ treats structs as POD if there is a user defined constructor, then that would be a problem - as it should not be treated it as POD, empty or non-empty.
Comment #8 by ibuclaw — 2013-12-21T08:17:26Z
(In reply to comment #7)
> (In reply to comment #5)
> > extern(C++) functions must do what the corresponding c++ compiler does.
>
> The definition of POD:
>
Also in written here: http://www.parashift.com/c++-faq-lite/pod-types.html
Comment #9 by yebblies — 2013-12-21T08:23:12Z
(In reply to comment #7)
> (In reply to comment #5)
> > extern(C++) functions must do what the corresponding c++ compiler does.
>
> The definition of POD:
>
> A PODS type in C++ is defined as either a scalar type or a PODS class. A PODS
> class has no user-defined copy assignment operator, no user-defined destructor,
> and no non-static data members that are not themselves PODS. Moreover, a PODS
> class must be an aggregate, meaning it has no user-declared constructors, no
> private nor protected non-static data, no base classes and no virtual
> functions.
>
>
>
> So if g++ treats structs as POD if there is a user defined constructor, then
> that would be a problem - as it should not be treated it as POD, empty or
> non-empty.
It really doesn't matter if C++ thinks it's not a POD, or D thinks it's not a POD. The System V ABI considers it a POD, and we need to match it.
Again, http://www.x86-64.org/documentation/abi.pdf
"If a C++ object has either a non-trivial copy constructor or a non-trivial
destructor [11], it is passed by invisible reference (the object is replaced in
the parameter list by a pointer that has class INTEGER)[12]"