Bug 19841 – Wrong ABI for C++ functions taking a struct by value

Status
RESOLVED
Resolution
WORKSFORME
Severity
blocker
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2019-05-02T12:11:58Z
Last change time
2020-09-07T22:41:48Z
Keywords
backend, C++, wrong-code
Assigned to
No Owner
Creator
Atila Neves

Comments

Comment #0 by atila.neves — 2019-05-02T12:11:58Z
Code: ------------------------ extern(C++) { struct Foo { void[32] _; } struct Bar { this(Foo foo); } } void oops() { auto foo = Foo(); auto bar = Bar(foo); } -------------------------- The assembly for oops doesn't even mention the RSI register where the address of `foo` should be passed in (RDI contains the address of where to construct `bar`). It tries to pass `foo` to the `Bar` constructor on the stack. The equivalent C++ code passes `foo` as an address in RSI. Calling the C++ implementation results in segfault. The bug is weirdly present in ldc and gdc as well.
Comment #1 by kinke — 2019-05-02T12:55:46Z
(In reply to Atila Neves from comment #0) > The equivalent C++ code passes `foo` as an address in RSI. No it doesn't: https://godbolt.org/z/hyzdMY
Comment #2 by atila.neves — 2019-05-02T14:03:02Z
Ah. In that case it's the copy ctor that causes this to happen: https://godbolt.org/z/HZGk6o
Comment #3 by atila.neves — 2019-05-02T14:06:35Z
It's possible that DIP1018 might change this.
Comment #4 by kinke — 2019-05-02T16:52:50Z
(In reply to Atila Neves from comment #2) > Ah. In that case it's the copy ctor that causes this to happen: Yes, as it's no POD in C++-terms anymore, affecting the ABI. So in D, you can define a dummy postblit to make it a non-POD. IIRC, Microsoft is a lot stricter, and the mere existence of at least one ctor makes it a non-POD.
Comment #5 by atila.neves — 2019-05-02T17:20:49Z
A dummy postblit wouldn't work - it wouldn't actually fix up the value. Duplicating the functionality of the C++ copy constructor is undesireable, and using the copy constructor with the latest release candidate of dmd 2.086.0 doesn't work either.
Comment #6 by bugzilla — 2020-09-07T04:55:15Z
D's definition of POD is: 1. it is not nested 2. it has no postblits, destructors, or assignment operators 3. it has no ref fields or fields that are themselves non-POD https://dlang.org/spec/struct.html#POD C++'s definition is quite a bit more complicated, and changes from one version of the Standard to another: https://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special/7189821#7189821 For reference, "the equivalent C++ code" is: struct Foo { char _[32]; }; struct Bar { Bar(Foo foo); }; void oops() { auto foo = Foo(); auto bar = Bar(foo); }
Comment #7 by bugzilla — 2020-09-07T05:04:12Z
Compiling the D version on Linux64 gives: _D5test84oopsFZv: push RBP mov RBP,RSP sub RSP,030h lea RAX,-028h[RBP] xor ECX,ECX mov [RAX],RCX mov 8[RAX],RCX mov 010h[RAX],RCX mov 018h[RAX],RCX mov -8[RBP],CL push dword ptr -010h[RBP] push dword ptr -018h[RBP] push dword ptr -020h[RBP] push dword ptr -028h[RBP] lea RDI,-8[RBP] call _ZN3BarC1E3Foo@PC32 add RSP,020h mov RSP,RBP pop RBP ret which looks equivalent to the C++ code generated by kinke's godbolt example: push rbp mov rbp, rsp sub rsp, 48 mov QWORD PTR [rbp-32], 0 mov QWORD PTR [rbp-24], 0 mov QWORD PTR [rbp-16], 0 mov QWORD PTR [rbp-8], 0 lea rax, [rbp-33] push QWORD PTR [rbp-8] push QWORD PTR [rbp-16] push QWORD PTR [rbp-24] push QWORD PTR [rbp-32] mov rdi, rax call Bar::Bar(Foo) add rsp, 32 nop leave ret So I'm going to mark this as worksforme.