How can I efficiently swap pointers in Delphi? I am trying to exchange pointers for integer type. The following example works however I2
is 0 when compiled with 64 bit.
program Project11;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
procedure Swap(const P1, P2);
asm
{$if defined(CPUX86)}
xchg ecx, [eax]
xchg ecx, [edx]
xchg [eax], ecx
{$else}
xchg rcx, [rax]
xchg rcx, [rdx]
xchg [rax], rcx
{$endif}
end;
var
I1, I2: Integer;
begin
I1 := 19;
I2 := 564;
WriteLn('Swapping..');
WriteLn('I1: '+I1.ToString());
WriteLn('I2: '+I2.ToString());
Swap(I1, I2);
WriteLn('I1: '+I1.ToString());
WriteLn('I2: '+I2.ToString());
ReadLn;
end.
I do it like this:
type
TGeneric = class
public
class procedure Swap<T>(var Left, Right: T); static;
end;
class procedure TGeneric.Swap<T>(var Left, Right: T);
var
temp: T;
begin
temp := Left;
Left := Right;
Right := temp;
end;
This can be used to swap values of any type. A capability that will make any Java programmers weep! ;-)
The compiler produces pretty efficient code in my opinion. I don't think you need to worry about the compiler's ability to produce effective code to perform assignments.
For 32 bit with T = Pointer
we have:
push ebx mov ecx,[eax] mov ebx,[edx] mov [eax],ebx mov [edx],ecx pop ebx ret
For 64 bit with T = Pointer
we have:
mov rax,[rcx] mov r8,[rdx] mov [rcx],r8 mov [rdx],rax ret
FWIW, I'm not keen on your versions use of const
parameters. The code in the question should use var
. But the type safety of this generic version is of course preferable.
David's answer is entirely the most sensible approach to solving the problem.
To explore why your code doesn't work however, note two things
Integer
is a 32-bit type for both x86 and x64RCX
, RDX
, R8
and R9
When compiling for x86 your code works. When compiling for X64 you are assuming that arguments are being passed to RAX
and RDX
, (like x86's EAX
, EDX
) which is wrong. So your code should look like
xchg rax, [rcx]
xchg rax, [rdx]
xchg [rcx], rax
You'll note that without changes this also fails - now both I1
and I2
are zero. This is because you've assumed that Integer
is a 64-bit type, which, unlike pointers, it is not (and you are not doing any type checking whatsoever - see again the benefit's of David's solution). If you redefine :
var
{$if defined(CPUX86)}
I1, I2: Integer;
{$else}
I1, I2: Int64;
{$ifend}
Then suddenly everything works as expected. By now, it should also be clear why this is not the most elegant approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With