Recent

Author Topic: Functions that return structures  (Read 3829 times)

Khrys

  • Sr. Member
  • ****
  • Posts: 256
Re: Functions that return structures
« Reply #15 on: May 26, 2025, 07:40:09 am »
No, it is a "unioned" structure.  One union consists of a HighPart and a LowPart and another, the QuadPart is an int64 but, the really important thing is that it is a _structure_.  An int64 is _never_ a structure.

No, its literally Int64. The structure is optional.

It absolutely is a structure; it just happens to match  Int64's  representation on little-endian machines:

Code: C  [Select][+][-]
  1. typedef union _LARGE_INTEGER {
  2.   struct {
  3.     DWORD LowPart;
  4.     LONG  HighPart;
  5.   } DUMMYSTRUCTNAME;
  6.   struct {
  7.     DWORD LowPart;
  8.     LONG  HighPart;
  9.   } u;
  10.   LONGLONG QuadPart;
  11. } LARGE_INTEGER;

It's clearly a holdover from pre-64-bit days, given the note at the top of the documentation page:

Quote from: LARGE_INTEGER union (winnt.h)
Note  Your C compiler may support 64-bit integers natively. For example, Microsoft Visual C++ supports the __int64 sized integer type. For more information, see the documentation included with your C compiler.

Using  stdcall  both FPC and GCC pass 64-bit integers in  EAX:EDX  while not doing so for small structs.

I suspect Microsoft used a bit of compiler magic to make MSVC recognize this exact struct as being equivalent to  int64_t, so I'm afraid there is no correct way to replicate this on the Pascal side (because it's never been correct in the first place).

440bx

  • Hero Member
  • *****
  • Posts: 5581
Re: Functions that return structures
« Reply #16 on: May 26, 2025, 08:25:38 am »
I suspect Microsoft used a bit of compiler magic to make MSVC recognize this exact struct as being equivalent to  int64_t,
Maybe that or, the 32bit MSVC compiler will return _any_ structure that is 64 bit in eax:edx.  If that's the case then it would be reasonable, for compatibility purposes, for FPC to do the same.

Disclaimer: I do not know if the 32bit MSVC (or other) compiler always returns 64 bit structures in the eax:edx pair.  As PascalDragon suggested, it might be worth investigating.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Khrys

  • Sr. Member
  • ****
  • Posts: 256
Re: Functions that return structures
« Reply #17 on: May 26, 2025, 08:57:02 am »
I suspect Microsoft used a bit of compiler magic to make MSVC recognize this exact struct as being equivalent to  int64_t,
Maybe that or, the 32bit MSVC compiler will return _any_ structure that is 64 bit in eax:edx.  If that's the case then it would be reasonable, for compatibility purposes, for FPC to do the same.

Disclaimer: I do not know if the 32bit MSVC (or other) compiler always returns 64 bit structures in the eax:edx pair.  As PascalDragon suggested, it might be worth investigating.

Comparing MSVC and GCC on Godbolt, there's a clear discrepancy in the handling of structs consisting of two 32-bit integer members.
I'm still inclined to think it's Microsoft bending the standard to their liking  :P

440bx

  • Hero Member
  • *****
  • Posts: 5581
Re: Functions that return structures
« Reply #18 on: May 26, 2025, 09:28:50 am »
@Khrys,

Interesting test.  Thank you for taking the time.

there's a clear discrepancy in the handling of structs consisting of two 32-bit integer members.
Yes and, that test makes it crystal clear.

I'm still inclined to think it's Microsoft bending the standard to their liking  :P
the standard certainly has something to say about how parameters are passed and who's responsible for clearing the stack but, I don't recall anything of importance about how structure values are _returned_.  It is reasonable to return a 64 bit value in the register pair eax:edx.  (Disclaimer: it's been a good number of years since I read a C standard and my memory hasn't gotten better with age.)

As it is currently, it looks like FPC matches the passing and returning done by GCC. 
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 17418
  • Ceterum censeo Trumpum esse delendum (Tnx Charlie)
Re: Functions that return structures
« Reply #19 on: May 26, 2025, 09:39:53 am »
msdn states that anything > 4 bytes and <= 8 bytes is generally returned in eax:edx for 32 bit intel.
"MSVC's 32-bit compiler typically returns 64-bit (8-byte) structures in eax:edx, as long as they are trivially copyable and fit within 8 bytes. Larger structures use a hidden pointer instead."
So it does depend on copy semantics and alignment in rare cases. So for simple unions <= 64 bit it it safe to assume it is the case.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

440bx

  • Hero Member
  • *****
  • Posts: 5581
Re: Functions that return structures
« Reply #20 on: May 26, 2025, 09:51:22 am »
A little searching dug out the following from

https://learn.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions?view=msvc-170

Quote
On x86 platforms, all arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair. Larger structures are returned in the EAX register as pointers to hidden return structures. Parameters are pushed onto the stack from right to left. Structures that are not PODs will not be returned in registers.

At least that's clear enough (though not very easy to find.)
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Thaddy

  • Hero Member
  • *****
  • Posts: 17418
  • Ceterum censeo Trumpum esse delendum (Tnx Charlie)
Re: Functions that return structures
« Reply #21 on: May 26, 2025, 09:57:40 am »
I found that too, but read the remarks I gave you about alignment and non-trivial copy behavior. That should be there too.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

PascalDragon

  • Hero Member
  • *****
  • Posts: 6039
  • Compiler Developer
Re: Functions that return structures
« Reply #22 on: May 27, 2025, 10:23:49 pm »
I found that too, but read the remarks I gave you about alignment and non-trivial copy behavior. That should be there too.

It's in the last sentence (well, at least the "non-trivial copy behavior" part):

Quote from: MSDN
Structures that are not PODs will not be returned in registers.

@440bx: Taking a quick peek at FPC's code I noticed this:

Code: Pascal  [Select][+][-]
  1.         case target_info.system of
  2.           system_i386_win32 :
  3.             begin
  4.               case def.typ of
  5.                 recorddef :
  6.                   begin
  7.                     { Win32 GCC returns small records in the FUNCTION_RETURN_REG up to 8 bytes in registers.
  8.  
  9.                       For stdcall and register we follow delphi instead of GCC which returns
  10.                       only records of a size of 1,2 or 4 bytes in FUNCTION_RETURN_REG }
  11.                     if ((pd.proccalloption in [pocall_stdcall,pocall_register]) and
  12.                         (def.size in [1,2,4])) or
  13.                        ((pd.proccalloption in cdecl_pocalls) and
  14.                         (def.size>0) and
  15.                         (def.size<=8)) then
  16.                      begin
  17.                        result:=false;
  18.                        exit;
  19.                      end;
  20.                   end;
  21.                 else
  22.                   ;
  23.               end;
  24.             end;
  25.  

I've just tested with Delphi 10.2 and it indeed does not pass the structure in EAX:EDX, but as a reference...  :-\

gues1

  • Jr. Member
  • **
  • Posts: 80
Re: Functions that return structures
« Reply #23 on: May 28, 2025, 12:30:16 am »
I've just tested with Delphi 10.2 and it indeed does not pass the structure in EAX:EDX, but as a reference...  :-\

Like in the code you quoted, Delphi (32 bit app) use reg EAX as result of struct up to 4 bytes (for example composed with two smallint), but with a function declared with a result of INT64 it use EAX:EDX.

In Microsoft note (I posted it in the third post of the topic) they suggest to use INT64 to wrap the function.

So, if a C function return up to 8 bytes structure data (like that originate the topic) then the unique way to wrap the result is declare it in pascal as "function ........  : INT64; stdcall; (or cdecl)"

Tested with Delphi Athens 12.3

440bx

  • Hero Member
  • *****
  • Posts: 5581
Re: Functions that return structures
« Reply #24 on: May 28, 2025, 07:45:46 am »
@440bx: Taking a quick peek at FPC's code I noticed this:

<snip>

I've just tested with Delphi 10.2 and it indeed does not pass the structure in EAX:EDX, but as a reference...  :-\
I completely understand the desire to be compatible with Delphi but, I feel that being compatible with the O/S should take precedence.

I don't use Delphi.. I use the O/S, a lot of people in that boat, that's why I think O/S compatibility should take precedence. 

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12314
  • FPC developer.
Re: Functions that return structures
« Reply #25 on: May 28, 2025, 10:41:22 am »
So this is possibly an ABI issue as that is documented by MSDN:

https://learn.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions?view=msvc-170&redirectedfrom=MSDN
https://blog.aaronballman.com/2012/02/describing-the-msvc-abi-for-structure-return-types/

Quote from: MSDN
On x86 platforms, all arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair.

FPC-on-windows originally closely aligned to GCC due to the use of binutils and the static linking of GDB into the IDE. 
« Last Edit: May 28, 2025, 11:15:02 am by marcov »

nanobit

  • Full Member
  • ***
  • Posts: 177
Re: Functions that return structures
« Reply #26 on: May 28, 2025, 05:38:30 pm »
Also stdcall methods of MS COM don't return struct-value by registers.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1536
    • Lebeau Software
Re: Functions that return structures
« Reply #27 on: May 28, 2025, 06:54:17 pm »
FYI, Delphi suffers from the same problem for 32-bit Windows:

https://en.delphipraxis.net/topic/11206-calling-c-dll-with-an-8-byte-struct-return-value-crashes-on-win32-works-on-win64/

A 64bit struct gets returned in a hidden var parameter rather than via CPU registers.  This is not aligned with the Windows 32bit ABI, which expects a 64bit struct to be returned in EAX:EDX. So, the workaround is as you already found - return a (U)Int64 (which does use EAX:EDX) and type-cast it.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

440bx

  • Hero Member
  • *****
  • Posts: 5581
Re: Functions that return structures
« Reply #28 on: May 28, 2025, 08:16:32 pm »
As I stated in a previous post, I don't think FPC should replicate Delphi's bugs/errors. 
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

gues1

  • Jr. Member
  • **
  • Posts: 80
Re: Functions that return structures
« Reply #29 on: May 28, 2025, 09:19:27 pm »
As I stated in a previous post, I don't think FPC should replicate Delphi's bugs/errors.
You are right.

But 25 and more years of this "wrong" ABI is difficult to "delete", impossible. May be FPC can add a new compiler option to adhere the correct ABI if is needed.

But change how the compiler acts is not so simple, there is POD evaluation and also ALIGN DATA to take in account.
I don't know ... it seems more complex than it appears.
« Last Edit: May 28, 2025, 09:21:03 pm by gues1 »

 

TinyPortal © 2005-2018