HTML'si Delphi _ftol inline assembly kodunu 64-bit derlemelere taşıma | losLab Software Development Blog

Teknik makale

Delphi _ftol inline assembly kodunu 64-bit derlemelere taşıma

· Delphi Programlama

Eski Delphi kodu bazen floating-point bir değeri integer'a dönüştürmek için küçük bir _ftol yardımcısı içerirdi. 32-bit projelerde bu tür bir helper'ı inline assembler ile yazmak yaygındı; özellikle eski derleyiciler için ayarlanmış grafik ve PDF kodunda.

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;

Aynı unit 64-bit hedef için derlendiğinde bu yaklaşım bozulur. Delphi 64-bit derleyici E1025 Unsupported language feature: 'ASM' bildirir, çünkü DCC64 normal Pascal rutinleri içinde Pascal ile inline assembly karışımını desteklemez.

64-bit derleyici bunu neden reddeder

DCC64, 64-bit assembly rutinlerini assemble edebilir, ancak rutin 64-bit calling convention düşünülerek tam bir assembler rutini olarak yazılmalıdır. 32-bit inline assembler kodunu bir Pascal fonksiyon gövdesine kopyalayıp derleyicinin register kullanımını, stack düzenini veya x87 davranışını çevirmesini bekleyemezsiniz.

Tam bir assembler sürümü mümkündür ve aşağıdaki gibi kod, değer zaten FPU stack üzerinde olduğunda gerekebilecek control-word işlemlerini gösterir.

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;

Hedef yalnızca truncation ise Pascal'ı tercih edin

Çoğu uygulama kodu için assembler rutini gereksizdir. Gerçek ihtiyaç bir Double değerini sıfıra doğru kırpmak ve integer değer döndürmekse, açık ve taşınabilir sürüm basitçe şudur:

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

Trunc niyeti doğrudan ifade eder, hem 32-bit hem de 64-bit hedefler için derlenir ve mimariye özgü assembler'ın bakım maliyetinden kaçınır. Gelecekteki bakımcıların range checking ve overflow davranışını düşünmesi de daha kolaydır.

Migration rehberi

  • Call site sizin kontrolünüzdeyse ve yalnızca sayısal dönüşüm gerekiyorsa Pascal implementasyonunu kullanın.
  • Assembler export'u yalnızca binary compatibility belirli bir symbol ve calling convention gerektirdiğinde koruyun.
  • Calling convention ve veri genişliğini incelemeden 32-bit register kodunu 64-bit bir rutine kopyalamayın.
  • Negatif değerler, büyük değerler ve hedef integer aralığı dışındaki değerler gibi edge case'leri test edin.

Aralık ve davranış konuları

Assembler'ı Trunc ile değiştirmek yine de test gerektiren bir davranış değişikliği olarak ele alınmalıdır. Kodunuzun negatif sayılar, integer sınırına yakın kesirler ve Integer aralığı dışındaki değerler için nasıl davranması gerektiğini doğrulayın. Eski assembler FPU control-word durumuna bağlı olmuş olabilir; Pascal sürümü ise hedef derleyicinin RTL implementasyonunu izler.

Helper bir PDF veya grafik komponentinin içindeyse, döndürülmüş metin, ölçeklenmiş koordinatlar ve dönüştürülmüş çizim komutları kullanan çıktıları test edin. Bu alanlar, basit örneklere göre float-to-integer dönüşüm farklarını daha kolay ortaya çıkarır.

Önerilen migration yolu

Önce mümkün olduğunda private dönüşüm helper'larını açık Pascal koduyla değiştirin. İkinci olarak, hâlâ gerekiyorsa özgün 32-bit implementasyonu yalnızca 32-bit conditional arkasında tutun. Üçüncü olarak, ayrı bir 64-bit branch'i yalnızca ölçülmüş performans veya binary compatibility gereksinimi haklı çıkarıyorsa ekleyin. Bu, taşınabilir yolu basit tutar ve mimariye özgü yolu varsayılan değil istisna yapar.