html Przenoszenie Delphi _ftol inline assembly do kompilacji 64-bitowych | losLab Software Development Blog

Artykuł techniczny

Przenoszenie Delphi _ftol inline assembly do kompilacji 64-bitowych

· Programowanie Delphi

Starszy kod Delphi czasem zawierał mały helper _ftol do konwersji wartości zmiennoprzecinkowej na liczbę całkowitą. W projektach 32-bitowych często pisano taki helper z użyciem inline assemblera, zwłaszcza w kodzie graficznym i PDF dostrajanym pod starsze kompilatory.

1
2
3
4
5
6
7
8
function _ftol( f: double) : Integer; cdecl;
begin
  asm
    lea   eax, f
    fstp  qword ptr [eax]
  end;
  result := Trunc(f);
end;

To podejście przestaje działać, gdy ta sama jednostka jest kompilowana dla celu 64-bitowego. Kompilator Delphi 64-bit zgłasza E1025 Unsupported language feature: 'ASM', ponieważ DCC64 nie obsługuje mieszania Pascala i inline assembly wewnątrz zwykłych procedur Pascala.

Dlaczego kompilator 64-bitowy to odrzuca

DCC64 może asemblować 64-bitowe procedury assembly, ale procedura musi być napisana jako kompletna procedura assemblera z uwzględnieniem 64-bitowej calling convention. Nie można skopiować 32-bitowego inline assemblera do ciała funkcji Pascala i oczekiwać, że kompilator przetłumaczy użycie rejestrów, układ stosu lub zachowanie x87.

Pełna wersja assemblerowa jest możliwa, a kod taki jak poniżej pokazuje rodzaj obsługi control-word, która może być wymagana, gdy wartość znajduje się już na stosie FPU.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function _ftol: Integer; cdecl;
// Assumes double value is in FPU stack on entry
// Make a truncation to integer and put it into function result
var
  TmpVal: Int64;
  SaveCW, ScratchCW: word;
 
asm
  .NOFRAME
  fnstcw word ptr [SaveCW]
  fnstcw word ptr [ScratchCW]
  or word ptr [ScratchCW], 0F00h  ;// trunc toward zero, full precision
  fldcw word ptr [ScratchCW]
  fistp qword ptr [TmpVal]
  fldcw word ptr [SaveCW]
  mov rax, TmpVal
end;

Wybierz Pascal, gdy celem jest tylko obcięcie

W większości kodu aplikacji procedura assemblerowa jest niepotrzebna. Jeśli rzeczywistym wymaganiem jest obcięcie Double w kierunku zera i zwrócenie wartości całkowitej, jasna i przenośna wersja to po prostu:

1
2
3
4
function _ftol(f: double): integer; cdecl;
begin
  Result := Trunc(f);
end;

Trunc wyraża zamiar bezpośrednio, kompiluje się dla celów 32- i 64-bitowych oraz eliminuje koszt utrzymania assemblera zależnego od architektury. Przyszłym opiekunom łatwiej też rozumieć sprawdzanie zakresu i zachowanie overflow.

Wskazówki migracyjne

  • Użyj implementacji w Pascalu, gdy kontrolujesz call site i potrzebujesz tylko konwersji liczbowej.
  • Zachowaj eksport assemblerowy tylko wtedy, gdy zgodność binarna wymaga konkretnego symbolu i calling convention.
  • Nie kopiuj 32-bitowego kodu rejestrowego do procedury 64-bitowej bez przeglądu calling convention i szerokości danych.
  • Testuj przypadki brzegowe, takie jak wartości ujemne, duże wartości i wartości poza zakresem docelowej liczby całkowitej.

Zakres i zachowanie

Zastąpienie assemblera przez Trunc nadal należy traktować jako zmianę zachowania wymagającą testów. Potwierdź, jak kod ma obsługiwać liczby ujemne, ułamki blisko granicy liczby całkowitej i wartości poza zakresem Integer. Stary assembler mógł zależeć od stanu FPU control-word, a wersja Pascalowa podąża za implementacją RTL dla docelowego kompilatora.

Jeśli helper znajduje się w komponencie PDF lub graficznym, testuj wyjście używające obróconego tekstu, skalowanych współrzędnych i transformowanych poleceń rysowania. Te obszary częściej ujawniają subtelne różnice konwersji float-to-integer niż proste przykłady.

Zalecana ścieżka migracji

Najpierw zastąp prywatne helpery konwersji jasnym kodem Pascala tam, gdzie to możliwe. Następnie zachowaj oryginalną implementację 32-bitową tylko za warunkiem 32-bitowym, jeśli nadal jest potrzebna. Dodaj osobną gałąź 64-bitową tylko wtedy, gdy uzasadnia to zmierzony wymóg wydajności lub zgodności binarnej. Dzięki temu ścieżka przenośna pozostaje prosta, a ścieżka zależna od architektury jest wyjątkiem, nie domyślnym rozwiązaniem.