In a new Win32 project, I have the following Delphi function:
procedure SetValue(value1, value2 : Extended);
begin
end;
In the same project but from a C++ unit, I call this function:
SetValue(10, 40);
When I check value1
when compiling with BCC32C (CLang), I get 1.68132090507504896E-4932, that is not correct.
Compiling with BCC32 (classic), I get 10.
The second parameter is 40 in both cases.
It seems that there is a problem with Extended
values and the parameter stack loading.
I use RAD Studio 10.1 Berlin.
How can I solve this?
UPDATE
I did not include the declaration because the hpp is created automatically when compiling. In any case, the declaration is:
extern DELPHI_PACKAGE void __fastcall SetValue(System::Extended value1, System::Extended value2);
To replicate the project:
1-Create a C++ project in Rad Studio
2-Add a Delphi unit with the above SetValue function
3-From C++ unit, add the hpp header with #include and call SetValue
It is all.
I need to use Extended type. I am using an external Delphi library, so I cannot change the types. The above code is a simplification of the problem. In reality, the problem is calling a function of this library that uses Extended in the parameters. Extended is a native type in Delphi but in C++ it is mapped as long double, 10 bytes (for Win32).
This seems to be an error in the BCC32C compiler. I guess it was not extended properly to handle Extended
as Delphi needs it.
If you look at the CPU window, then the BCC32 compiler generates:
File5.cpp.14: SetVal(10.0L, 40.0L);
00401B8F 6802400000 push $00004002
00401B94 68000000A0 push $a0000000
00401B99 6A00 push $00
00401B9B 6804400000 push $00004004
00401BA0 68000000A0 push $a0000000
00401BA5 6A00 push $00
00401BA7 E80C000000 call Unit12::SetVal(long double,long double)
and that is correct. It first pushes 10
, then 40
in Extended
format. Note that each Extended
occupies 12 byte on the stack.
But now look at the output for the BCC32C compiler:
File5.cpp.14: SetVal(10.0L, 40.0L);
00401B5D 89E0 mov eax,esp
00401B5F D90554F14E00 fld dword ptr [$004ef154]
00401B65 DB38 fstp tbyte ptr [eax]
00401B67 D90558F14E00 fld dword ptr [$004ef158]
00401B6D DB780A fstp tbyte ptr [eax+$0a]
00401B70 E81F000000 call Unit12::SetVal(long double,long double)
00401B75 83EC18 sub esp,$18
It first reads the 32 single precision float 40
and stores it as Extended
at [ESP]
. So far, so good. But then it reads in the next 32 bit single precision float 10
(still OK) but then stores it at [ESP+$0A]
, which is clearly wrong (for Delphi)! It should be stored at [ESP+$0C]
! That is why the first value, which is read by the Pascal function at [ESP+$0C]
, but which is stored by BCC32C at [ESP+$0A]
, is wrong.
So this seems to be a bug. Reported as https://quality.embarcadero.com/browse/RSP-15737
Note that this is the normal way BCC32C pushes and expects such values. In a C++ function in the same module, i.e. compiled with BCC32C too, this works nicely:
void __fastcall Bla(long double a, long double b)
{
printf("%Lf %Lf\n", a, b);
}
But Delphi expects a 10 byte Extended
to occupy 12 bytes on the stack, not 10 bytes as BCC32C does.
Weirdly enough, if the function to be called is not a Delphi __fastcall
function, but a normal C++ (cdecl
) function, the BCC32C compiler will store the Extended
s (long double
s) in [ESP+$0C]
and [ESP]
respectively.
As David Heffernan commented, you can pass multiple extendeds in a record. Alternatively, you could pass them as var
parameters. In both cases, it is not as simple as calling SetVal(10.0, 40.0);
.