访问64位内联汇编程序中的Delphi类字段

时间:2015-03-26 18:28:15

标签: delphi 64-bit inline-assembly delphi-xe4

我正在尝试将Delphi TBits.GetBit转换为64位版本的内联汇编程序。 VCL源代码如下:

function TBits.GetBit(Index: Integer): Boolean;
{$IFNDEF X86ASM}
var
  LRelInt: PInteger;
  LMask: Integer;
begin
  if (Index >= FSize) or (Index < 0) then
    Error;

  { Calculate the address of the related integer }
  LRelInt := FBits;
  Inc(LRelInt, Index div BitsPerInt);

  { Generate the mask }
  LMask := (1 shl (Index mod BitsPerInt));
  Result := (LRelInt^ and LMask) <> 0;
end;
{$ELSE X86ASM}
asm
    CMP     Index,[EAX].FSize
    JAE     TBits.Error
    MOV     EAX,[EAX].FBits
    BT      [EAX],Index
    SBB     EAX,EAX
    AND     EAX,1
end;
{$ENDIF X86ASM}

我开始将32位ASM代码转换为64位。经过一些搜索,我发现我需要为64位编译器更改对RAX的EAX引用。我在第一行结束了这个:

CMP     Index,[RAX].FSize

这会编译但在运行时会发出访问冲突。我尝试了一些组合(例如MOV ECX,[RAX].FSize)并在尝试访问[RAX].FSize时获得了相同的访问冲突。当我查看由Delphi编译器生成的汇编程序时,我的[RAX].FSize看起来应该是正确的。

Unit72.pas.143: MOV     ECX,[RAX].FSize
00000000006963C0 8B8868060000     mov ecx,[rax+$00000668]

Delphi生成的代码:

Unit72.pas.131: if (Index >= FSize) or (Index < 0) then
00000000006963CF 488B4550         mov rax,[rbp+$50]
00000000006963D3 8B4D58           mov ecx,[rbp+$58]
00000000006963D6 3B8868060000     cmp ecx,[rax+$00000668]
00000000006963DC 7D06             jnl TForm72.GetBit + $24
00000000006963DE 837D5800         cmp dword ptr [rbp+$58],$00
00000000006963E2 7D09             jnl TForm72.GetBit + $2D

在这两种情况下,生成的汇编程序都使用[rax+$00000668]进行FSize。在Delphi 64bit汇编程序中访问类字段的正确方法是什么?

这可能听起来像是一个奇怪的事情,但64位pascal版本的汇编程序看起来效率不高。我们称这个例程很多次,执行时间长达5倍,具体取决于各种因素。

1 个答案:

答案 0 :(得分:3)

基本问题是你使用了错误的寄存器。 Self在所有其他参数之前作为隐式参数传递。在x64 calling convention中,这意味着它会在RCX而不是RAX中传递。

因此Self传递RCXIndex传递RDX。坦率地说,我认为在内联汇编程序中使用参数名称是错误的,因为它们隐藏了参数在寄存器中传递的事实。如果您碰巧覆盖RDX,则会更改Index的表观值。

因此if语句可能被编码为

CMP     EDX,[RCX].FSize
JNL     TBits.Error
CMP     EDX,0
JL      TBits.Error

FWIW,这是一个非常简单的实现功能,我不相信你需要使用任何堆栈空间。在x64中有足够的寄存器可以完全使用易失性寄存器完成。

相关问题