Bug 23195 – Win64 function ABI bug for small non-POD arguments

Status
RESOLVED
Resolution
FIXED
Severity
critical
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Windows
Creation time
2022-06-18T22:39:20Z
Last change time
2023-02-09T07:39:08Z
Keywords
backend, C++, pull, wrong-code
Assigned to
No Owner
Creator
simon.vanbernem
See also
https://issues.dlang.org/show_bug.cgi?id=19563

Comments

Comment #0 by simon.vanbernem — 2022-06-18T22:39:20Z
Microsoft states in their C++ x64 calling convention that structs with a maximum size of 8 bytes should be passed by value through registers (POD or not doesn't matter). Dmd gets this wrong, and passes them by reference instead, when calling a function marked with extern(C++) that has non-POD arguments of size 8. The called C++ function will then interpret the pointers in the registers as values and compute nosense. Ldc doesn't have this bug and does the right thing. For example, for the following definitions: void imgui_draw_rectangle(ImVec2 min, ImVec2 max); struct ImVec2{ float x, y; ~this(){} } calling imgui_draw_rectangle will load the addresses of min and max into rdx and rcx, instead of loading their values into rdx and rcx. (https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#parameter-passing)
Comment #1 by bugzilla — 2022-11-24T02:27:59Z
Compilable example: struct FF { float x, y; ~this(); } void draw(FF min, FF max); void test(FF *a, FF *b) { draw(*a, *b); } If `~this();` is commented out, the arguments are passed to draw by value.
Comment #2 by dlang-bot — 2022-11-24T02:39:19Z
@WalterBright created dlang/dmd pull request #14651 "fix Issue 23195 - Win64 function ABI bug for small non-POD arguments" fixing this issue: - fix Issue 23195 - Win64 function ABI bug for small non-POD arguments https://github.com/dlang/dmd/pull/14651
Comment #3 by bugzilla — 2022-11-24T07:05:00Z
This problem appears to have been introduced by https://github.com/dlang/dmd/pull/9434/
Comment #4 by bugzilla — 2022-11-24T18:00:22Z
(In reply to simon.vanbernem from comment #0) > Microsoft states in their C++ x64 calling convention that structs with a > maximum size of 8 bytes should be passed by value through registers (POD or > not doesn't matter). > [...] > (https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc- > 170#parameter-passing) Microsoft's documentation is doesn't match the behavior of their compiler. Having a destructor does not affect whether a struct is passed by ref or not, but having a copy constructor does (contrary to the documentation).
Comment #5 by kinke — 2022-11-24T23:24:30Z
(In reply to Walter Bright from comment #4) > Microsoft's documentation is doesn't match the behavior of their compiler. > Having a destructor does not affect whether a struct is passed by ref or > not, but having a copy constructor does (contrary to the documentation). That's right. Note that there's another special case for the MSVC++ ABI: POD structs with any ctor are returned via struct-return (the hidden result pointer). https://github.com/ldc-developers/ldc/blob/05fb6d5acaef1a97129317ce8f0dd712e03aee7f/gen/abi/win64.cpp#L59-L80
Comment #6 by dlang-bot — 2023-02-09T07:39:08Z
dlang/dmd pull request #14651 "fix Issue 23195 - Win64 function ABI bug for small non-POD arguments" was merged into master: - 9a6324fa38859c5ad67592f590234cbb36ccfbb2 by Walter Bright: fix Issue 23195 - Win64 function ABI bug for small non-POD arguments https://github.com/dlang/dmd/pull/14651