Recent

Author Topic: Accessing an object (self) from x86-64 asm?  (Read 6391 times)

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #15 on: January 18, 2020, 05:11:44 pm »
Well, try. The reason that ebx / rbx is non-volatile is because it holds self.

p.s. 64-bit calling conventions differ between targets. (win64 vs *nix)

Thaddy, are you on Linux? That would explain the difference, I'm on Win7-64.

avk

  • Hero Member
  • *****
  • Posts: 752
Re: Accessing an object (self) from x86-64 asm?
« Reply #16 on: January 18, 2020, 05:36:10 pm »
Extremely useful resource https://www.agner.org/optimize/

BTW on Linux it passes in RDI. :)

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Accessing an object (self) from x86-64 asm?
« Reply #17 on: January 18, 2020, 09:21:00 pm »
Self is in RBX!
Your answer is wrong. On both Win64 and SysV ABIs. On Win64 the first non-floating point parameter is passed in RCX and on Sys V it's passed in RDI. For both ABIs RBX is considered non-volatile.

Thanks all. Just tested accessing the object (self) data field via [rcx] and that works. :)
You can simplyfy the assembly code a bit more (FPC's assembler readers are quite capable ;) ):
Code: Pascal  [Select][+][-]
  1. Type Ttest = object
  2.                lowQ : qword;
  3.                highQ : qword;
  4.                { the "nostackframe" is important! }
  5.                procedure getLow(var a : qword); assembler; nostackframe; register;
  6.              end;
  7.  
  8. procedure Ttest.getLow(var a : qword); assembler; nostackframe; register;
  9. asm
  10.    mov rax, Self.lowQ // get self.lowQ
  11.    mov a, rax  // store result in var param "a"
  12. end;
  13.  

This will result in the following assembly code for the whole of getLow:

Code: ASM  [Select][+][-]
  1. # [13] asm
  2. # [15] mov rax, Self.lowQ // get self.lowQ
  3.         movq    %rcx,%rax
  4. # [16] mov a, rax  // store result in var param "a"
  5.         movq    %rax,%rdx
  6. # [17] end;
  7.         ret
  8.  

RAX can be used because it is a volatile register and is intended for the function result anyway. This way you don't even need to manipulate the stack.

Though I don't know why you don't let the compiler simply do its magic and access lowQ directly?

Code: Pascal  [Select][+][-]
  1. var
  2.   t: TTest;
  3.   q: QWord;
  4. begin
  5.   t.lowQ := 42;
  6.   q := t.lowQ;
  7. end.

Code: ASM  [Select][+][-]
  1. # [24] q := t.lowQ;
  2.         movq    U_$P$TASMTEST_$$_T(%rip),%rax
  3.         movq    %rax,U_$P$TASMTEST_$$_Q(%rip)

This way there won't even be a call.

And if you should have more complex code using Pascal code and inline would be better anyway (though as of now inlining won't work if var or out parameters are involved, so you should use Result in that case).

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #18 on: January 19, 2020, 04:00:26 am »

Code: ASM  [Select][+][-]
  1. # [24] q := t.lowQ;
  2.         movq    U_$P$TASMTEST_$$_T(%rip),%rax
  3.         movq    %rax,U_$P$TASMTEST_$$_Q(%rip)

This way there won't even be a call.

Thanks for all your help PascalDragon.  :)

I should point out that the code I posted was just a stripped down stub to illustrate my immediate problem (of not knowing what register passed the reference to self). It's not my actual code, and I would never have really used asm for such a simple method. I very much appreciate your help and you've pointed out some valuable things to me, but there's no point critiquing that particular code too much, because it was never going to be used anyway.

The only reason I posted any code in the first place (rather than just asking "hey guys, what register passes the reference to self for asm methods in Win64/X64") was that I wasn't completely sure that it wasn't a more fundamental issue. So, while I was 99% that it was just a change in register calling convention tripping me up, I couldn't totally rule out that it wasn't some other misunderstanding regarding the change to x64. So I just posted a "made up" method that had the code stub which I knew would trigger the same problem.

Hope that makes sense.  :)
« Last Edit: January 19, 2020, 04:06:44 am by uart »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Accessing an object (self) from x86-64 asm?
« Reply #19 on: January 19, 2020, 10:06:35 am »
In that case you should keep in mind what marcov said and only fall back to assembly if you really notice the Pascal code to be slow. By declaring a method as inline and playing with the various optimizations that FPC provides you should in most cases get a good result. And only if that is not sufficient you should go the assembly route. And you should always have a Pascal variant of your code anyway, cause this way you can easily port your code to other platforms (in this case it would already fail on any non-Win64 x86_64 system). A common approach for this is the following:

Code: Pascal  [Select][+][-]
  1. procedure SomeMethod;
  2. {$ifdef PUREPASCAL}
  3. begin
  4. ...
  5. end;
  6. {$else} nostackframe; assembler;
  7. asm
  8. {$if defined(CPU386)}
  9. ...
  10. {$elseif defined(CPUX86_64)}
  11. ...
  12. {$elseif defined(CPUARM)}
  13. ...
  14. {$else}
  15. {$fatal No assembly code defined}
  16. {$endif}
  17. end;{$endif}

Also using the -al you can view the assembly code that the compiler generated (in the unit output directory as (for most platforms) *.s files).

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Accessing an object (self) from x86-64 asm?
« Reply #20 on: January 20, 2020, 10:41:09 am »
Funny thing. I also wanted to suggest something like this:
Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword); assembler; register;
  2. asm
  3.    mov rax,[Self].lowQ;
  4.    mov [a],rax;
  5. end;    
  6.  
But it doesn't work. It says "Unknown identifier Self".

This one works, but works incorrectly, becuase of "mov rax, rcx" instead of "mov rax, [rcx]"
Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword); assembler; register;
  2. asm
  3.    mov rax,Self.lowQ;
  4.    mov [a],rax;
  5. end;    
  6.  

But all of a sudden this works correctly!
Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword); assembler; register;
  2. asm
  3.    mov rax,Self.highQ;
  4.    mov [a],rax;
  5. end;    
  6.  
« Last Edit: January 20, 2020, 12:26:39 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Accessing an object (self) from x86-64 asm?
« Reply #21 on: January 20, 2020, 10:08:57 pm »
This one works, but works incorrectly, becuase of "mov rax, rcx" instead of "mov rax, [rcx]"
Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword); assembler; register;
  2. asm
  3.    mov rax,Self.lowQ;
  4.    mov [a],rax;
  5. end;    
  6.  
Hadn't noticed that when I adjusted the code. :o That would need to be fixed, probaly it's related to lowQ being at offset 0 as your other example with highQ works...  :'(

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Accessing an object (self) from x86-64 asm?
« Reply #22 on: January 21, 2020, 07:02:56 am »
Hadn't noticed that when I adjusted the code. :o That would need to be fixed, probaly it's related to lowQ being at offset 0 as your other example with highQ works...  :'(
I will try it in Delphi, when I'll have time for this. This is actually so called TASM syntax. And of course Delphi inherits it. "Self" and "a" are actually declared as var, i.e. they aren't variables themselves - they're pointers to variables. For example Self = rcx and a = rdx. They should be dereferenced via []. But all of a sudden it doesn't work. It works for "a", but doesn't for "Self". "Self + x" somehow forces it to be [Self + x], just because "mov rax, rcx + 8" isn't valid, but "mov rax, [rcx + 8]" is. This is all wrong anyway. "[Self]" just should work. Otherwise only possible crutch is:
Code: Pascal  [Select][+][-]
  1. mov rax, Self
  2. mov rax, [rax]
  3. mov [a], rax
  4.  
That is waste of one instruction.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #23 on: January 22, 2020, 07:17:39 am »
Thanks for finding that Mr Madguy. It definitely looks like a bug.

In summary, it appears that it fails to dereference the member variable if that member variable happens to coincide (in address) with the base reference to the object. That is, it seems to treat self.lowQ identically with self.

I also tested this on an older version (fpc 2.6.0) with 32 bit code (and of course dwords instead of qwords, but otherwise identical), and the results were the same.  I even tried making the member variable a different size to a pointer (eg byte or word), but it still failed to dereference the base member variable.

BTW. When I tested this on Delphi 7 (again with 32bit asm as above), it worked exactly as expected. It correctly dereferenced both self.lowD and self.highD, but (as expected) didn't dereference self alone (eg "mov eax,self").

Just wondering if this bug should be reported somewhere?
« Last Edit: January 22, 2020, 07:33:21 am by uart »

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Accessing an object (self) from x86-64 asm?
« Reply #24 on: January 22, 2020, 08:20:40 am »
Thanks for finding that Mr Madguy. It definitely looks like a bug.

In summary, it appears that it fails to dereference the member variable if that member variable happens to coincide (in address) with the base reference to the object. That is, it seems to treat self.lowQ identically with self.

I also tested this on an older version (fpc 2.6.0) with 32 bit code (and of course dwords instead of qwords, but otherwise identical), and the results were the same.  I even tried making the member variable a different size to a pointer (eg byte or word), but it still failed to dereference the base member variable.

BTW. When I tested this on Delphi 7 (again with 32bit asm as above), it worked exactly as expected. It correctly dereferenced both self.lowD and self.highD, but (as expected) didn't dereference self alone (eg "mov eax,self").

Just wondering if this bug should be reported somewhere?
It does simple thing. Something like this:
Code: Text  [Select][+][-]
  1. Self equ rcx
  2. lowQ equ 0
  3. highQ equ 8
  4.  
May be typed, because otherwise something like "mov [Self].lowQ, 0" would generate "ambiguous type" error. I just don't remember exact TASM syntax for it. It was so long ago. All other things should be done by you. I.e. if you want to dereference pointer - you need to use []. And "[Self].lowQ" is simply equal to "[Self.lowQ]" and "[Self + lowQ]". It's just some syntax sugar to make code more readable.

There should be some bug in FPC, because in Delphi everything works as expected.
« Last Edit: January 22, 2020, 08:22:38 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Accessing an object (self) from x86-64 asm?
« Reply #25 on: January 22, 2020, 09:30:33 am »
Please report a bug (with an example that should work) so that it isn't forgotten.
Maybe you can also try whether the AT&T syntax is handling this correctly?

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Accessing an object (self) from x86-64 asm?
« Reply #26 on: January 22, 2020, 10:10:23 am »
Please report a bug (with an example that should work) so that it isn't forgotten.
Maybe you can also try whether the AT&T syntax is handling this correctly?
Code: Pascal  [Select][+][-]
  1. procedure TTest.GetLo(var A:QWord);assembler;nostackframe;
  2. asm
  3.   mov rax,[Self].lo
  4.   mov [A],rax
  5. end;
  6.  
Doesn't work, but should. Says "Unknown identifier "Self"".

AT&T syntax doesn't work at all. If I use (A) - it says "Invalid reference syntax". And I don't know, how to do it correctly. If it's possible.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #27 on: January 22, 2020, 10:14:35 am »
I'm not really familiar with AT&T syntax, but I had a go and it just says "invalid reference" whatever I try (when trying to access either "self" or "a").

eg (32 bit code) move register value to var parameter "a".

mov %eax, (a)     <-----  "invalid reference"
mov %eax, 0(a)   <-----  "invalid reference"
mov %eax, a       <-----  "invalid reference"

Edit: You replied while I was typing Mr Madguy, but it looks like we came to the same conclusion.
« Last Edit: January 22, 2020, 10:18:10 am by uart »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Accessing an object (self) from x86-64 asm?
« Reply #28 on: January 23, 2020, 10:03:11 am »
The following works, though I couldn't get Self to work either, so maybe that's a shortcoming of the AT&T reader.

Code: Pascal  [Select][+][-]
  1. procedure TTest.GetHi(var A: LongInt); assembler;
  2. asm
  3.   mov TTest.Hi(%eax), %eax
  4.   mov %eax, A
  5. end;

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #29 on: January 23, 2020, 11:08:35 am »
The following works, though I couldn't get Self to work either, so maybe that's a shortcoming of the AT&T reader.

Code: Pascal  [Select][+][-]
  1. procedure TTest.GetHi(var A: LongInt); assembler;
  2. asm
  3.   mov TTest.Hi(%eax), %eax
  4.   mov %eax, A
  5. end;

That code compliles ok, but it doesn't run correctly for me (this is my older 32bit Windows computer with fpc 2.6.0). It does not transfer the correct value to the "var A" variable.

I'm not 100% certain, but I think that the line "mov %eax, A" actually just does the same as "mov %eax, %edx". That is, a register to register move instead of register to memory.

 

TinyPortal © 2005-2018