2015-03-26 2 views
3

Я пытаюсь преобразовать Delphi TBits.GetBit в встроенный ассемблер для 64-разрядной версии. Источник VCL выглядит так:Доступ к полям класса Delphi в 64-битном встроенном ассемблере

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-разрядный. После некоторого поиска я узнал, что мне нужно изменить ссылки EAX на RAX для 64-битного компилятора. Я закончил с этим для первой строки:

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 Assembler?

Это может показаться странным для оптимизации, но ассемблер для 64-битной версии паскаля выглядит не очень эффективным. Мы называем эту процедуру большим количеством раз, и для выполнения в любом случае требуется в 5 раз больше времени, в зависимости от различных факторов.

+0

У вас есть версия pascal под рукой? –

+0

@DavidHeffernan Положите его в вопрос. Не хотел ставить слишком много источников Delphi VCL в вопрос. 64-разрядная версия не то же самое. – Graymatter

+0

Это код VCL, не так ли? В этом случае я могу найти его. –

ответ

3

Основная проблема заключается в том, что вы используете неправильный регистр. Self передается как неявный параметр перед всеми остальными. В x64 calling convention это означает, что оно передается в RCX, а не RAX.

Номер Self принят в RCX и Index принят в RDX. Честно говоря, я думаю, что ошибочно использовать имена параметров в встроенном ассемблере, потому что они скрывают тот факт, что параметр был передан в регистр. Если вам случится перезаписать либо RDX, то это изменит кажущееся значение Index.

Так if заявление может быть закодирован как

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

FWIW, это очень простая функция для реализации, и я не верю, что вам нужно будет использовать любое пространство стека. У вас достаточно регистров в x64, чтобы иметь возможность делать это полностью с помощью изменчивых регистров.

+0

Хорошо, это имеет смысл. Я удивлен, что ассемблер, сгенерированный компилятором Delphi, использует RAX в его «CMP» вместо использования RCX. – Graymatter

+0

Вы ищете не оптимизированный код. Посмотрите на asm, созданный при включении оптимизатора. FWIW, я не думаю, что код плохой, но он выполняет два деления, когда их хватит. Я бы рассмотрел его кодирование в Pascal, но использовал функцию 'DivMod'. Шахта выглядит так: –

+1

'процедура DivMod (дивиденд, дивизор: кардинал, вне Котировочный, остаточный: кардинал); {$ IFDEF CPUX86} ASM PUSH EBX MOV EBX, EDX XOR EDX, EDX DIV EBX MOV [ECX], EAX MOV EBX, относящиеся к MOV [EBX], EDX POP EBX конец; {$ ИНАЧЕ ЕСЛИ Defined (CPUX64)} ASM .NOFRAME MOV EAX, ECX MOV ECX, EDX XOR EDX, EDX ДИВ ЕСХ ДВИЖЕНИЯ [С8], EAX MOV [R9], EDX конец; {$ ELSE} {$ Сообщение об ошибке 'Непризнанный платформа'.} {$ ENDIF} ' –

Смежные вопросы