Recent

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

uart

  • Jr. Member
  • **
  • Posts: 58
Accessing an object (self) from x86-64 asm?
« on: January 18, 2020, 03:19:36 pm »
Previously when I tested accessing the data fields in a simple static fpc object with a x86-32bit target, I could access "self" using the [eax] pointer.

Now I'm trying to move my code to an x86-64bit target and it doesn't appear to work the same way (something different in the calling conventions?).

A simplified example of the code that works ok on the x86 target would be something like this:
Code: [Select]
program testasm;
{$mode objfpc}{$H+}
{$ASMMODE   intel}
uses Classes, sysutils;

Type Ttest = object
               lowD : dword;
               highD : dword;
               procedure getLow(var a : dword); assembler; register;
             end;

procedure Ttest.getLow(var a : dword); assembler; register;
asm
   push ecx;
   mov ecx,[eax];     // get self.lowD
   mov [edx],ecx;     // store result in var param "a"
   pop ecx;
end;

Obviously this is a trivial example just to make sure that I can access parameters as expected, but when I do the equivalent with a Win64 and x86-64 target it compiles ok, but either crashes with a runtime error or returns garbage.

Here is a trivial example of the 64 bit code that doesn't work.
Code: [Select]
program testasm64;
{$mode objfpc}{$H+}
{$ASMMODE   intel}
uses Classes, sysutils;

Type Ttest = object
               lowQ : qword;
               highQ : qword;
               procedure getLow(var a : qword); assembler; register;
             end;

procedure Ttest.getLow(var a : qword); assembler; register;
asm
   push rcx;
   mov rcx,[eax];     // get self.lowQ
   mov [rdx],rcx;     // store result in var param "a"
   pop rcx;
end;

This one compiles ok (fpc 3.04 cross compiler with x86-64 target CPU and Win64 target OS) but the if the method getLow is called it either runtime crashes at the asm line "mov rcx,[rax]" or returns a garbage result (at the same asm line).

So just wondering if the register calling convention different with the x86-64 compiler compared to the regular x86?

BTW. I am running the code on an Intel Core i5, under Windows 7 64 bit OS.
« Last Edit: January 18, 2020, 03:33:32 pm by uart »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Accessing an object (self) from x86-64 asm?
« Reply #1 on: January 18, 2020, 03:33:26 pm »
[eax] sounds like you are dereferencing a 32-bit pointer?

And 64-bit has stack alignment so random pushing and popping is not so good. I usually use a construct like this:

Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword);
  2. begin
  3. asm
  4.  
  5.    mov rcx,[rax];     // get self.lowQ
  6.    mov [rdx],rcx;     // store result in var param "a"
  7. end['rcx];
  8. end;
  9.  

And in case of doubt look at generated code with -al for original pascal and asm code. Note that small fragments of asm are not inlinable while the pascal equivalent is.  _Benchmark_ before doing asm.

avk

  • Hero Member
  • *****
  • Posts: 752
Re: Accessing an object (self) from x86-64 asm?
« Reply #2 on: January 18, 2020, 03:43:30 pm »
On win64 first parameter passes in RCX.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Accessing an object (self) from x86-64 asm?
« Reply #3 on: January 18, 2020, 03:48:45 pm »
On win64 first parameter passes in RCX.

True and the parameter ones don't need saving anyway:

// Integer arguments are passed in registers RCX, RDX, R8, and R9.
// Floating point arguments are passed in XMM0L, XMM1L, XMM2L, and XMM3L.
// volatile: RAX, RCX, RDX, R8, R9, R10, R11
// nonvolatile RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile
// volatile parameter registers xmm0-xmm3 and xmm4,5
// https://msdn.microsoft.com/en-us/library/ms235286.aspx

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #4 on: January 18, 2020, 03:51:20 pm »
I planned to ask about alternate ways to save registers later. Also benchmarking later, right now I'm just learning.

There are some 64 bit features that I'm not sure how to access outside of assembler, so I want to learn how the calling convention for asm works.

The freepascal help said that when register passing was used that the first three parameters where passed in eax, edx and ecx (this was for 32 bit). It further said that if the asm procedure was an object method that "self" was passed as a hidden parameter. It didn't say which one, but I guessed eax and that worked. So [eax] pointed to the first data field in the static object.

The same information apparently doesn't translate to 64bit assembler. [rax] does not seem to point to the first data field in this case, and I'm trying to figure out what does.
« Last Edit: January 18, 2020, 03:57:29 pm by uart »

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #5 on: January 18, 2020, 03:52:45 pm »
On win64 first parameter passes in RCX.

Good stuff avk. I will try that right away.

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #6 on: January 18, 2020, 03:56:08 pm »

True and the parameter ones don't need saving anyway:

// Integer arguments are passed in registers RCX, RDX, R8, and R9.
// Floating point arguments are passed in XMM0L, XMM1L, XMM2L, and XMM3L.
// volatile: RAX, RCX, RDX, R8, R9, R10, R11
// nonvolatile RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile
// volatile parameter registers xmm0-xmm3 and xmm4,5
// https://msdn.microsoft.com/en-us/library/ms235286.aspx

Good info. Actually I suspected that those register didn't need to be saved but was playing it saffe (I've done this before but not for a very long time). :)
I had planned to ask exactly those type questions once I got some code up and running, but I just got stuck at the first step with the change in register passing convention.
« Last Edit: January 18, 2020, 03:59:41 pm by uart »

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #7 on: January 18, 2020, 04:08:50 pm »
Thanks all. Just tested accessing the object (self) data field via [rcx] and that works. :)

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: Accessing an object (self) from x86-64 asm?
« Reply #8 on: January 18, 2020, 04:10:31 pm »
Self is in RBX!
Specialize a type, not a var.

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #9 on: January 18, 2020, 04:12:42 pm »
Note that small fragments of asm are not inlinable while the pascal equivalent is.  _Benchmark_ before doing asm.

Thanks, but can you just clarify this point for me. Do you mean not using
Code: [Select]
procedure x; assembler
asm
...
end

but instead using
Code: [Select]
procedure;
begin
  asm
  ...
  end {asm}
end {procedure}

Is that correct?
« Last Edit: January 18, 2020, 04:15:57 pm by uart »

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #10 on: January 18, 2020, 04:18:27 pm »
Self is in RBX!
It doesn't appear to be. Avk's advice to use rcx worked for me. When I tested, [rcx] accessed the low qword field and [rcx+8] accessed the high qword field.
« Last Edit: January 18, 2020, 04:20:13 pm by uart »

avk

  • Hero Member
  • *****
  • Posts: 752
Re: Accessing an object (self) from x86-64 asm?
« Reply #11 on: January 18, 2020, 04:34:09 pm »
In your previous topic, I posted a link that may seem interesting to you.

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: Accessing an object (self) from x86-64 asm?
« Reply #12 on: January 18, 2020, 04:39:40 pm »
Self is in RBX!
It doesn't appear to be. Avk's advice to use rcx worked for me. When I tested, [rcx] accessed the low qword field and [rcx+8] accessed the high qword field.
Well, try. The reason that ebx / rbx is non-volatile is because it holds self.
Specialize a type, not a var.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Accessing an object (self) from x86-64 asm?
« Reply #13 on: January 18, 2020, 04:55:51 pm »
Note that small fragments of asm are not inlinable while the pascal equivalent is.  _Benchmark_ before doing asm.

Thanks, but can you just clarify this point for me. Do you mean not using

No, more that for many short procedures assembler to microoptimize stack generation and self usage is not worthwhile if you can simply inline the pascal versions, and the compiler will do it for you.

I do some assembler for work, but most of my assembler processes whole images or at least a line, so quite long functions, like this one to rotate an 8-bit image: http://www.stack.nl/~marcov/rot8x8.txt

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

uart

  • Jr. Member
  • **
  • Posts: 58
Re: Accessing an object (self) from x86-64 asm?
« Reply #14 on: January 18, 2020, 05:06:22 pm »
No, more that for many short procedures assembler to microoptimize stack generation and self usage is not worthwhile if you can simply inline the pascal versions, and the compiler will do it for you.

Ok I see what you mean. Write short procedures/functions is straight pascal where possible and declare them with the inline attribute.

Code: [Select]
{$ INLINE ON}

procedure x; inline;
begin
 ... {straight pascal}
end;

 

TinyPortal © 2005-2018