كان كود Delphi القديم يحتوي أحيانا على مساعد صغير باسم _ftol لتحويل قيمة فاصلة عائمة إلى عدد صحيح. في مشاريع 32 بت كان من الشائع كتابة هذا النوع من المساعد باستخدام inline assembler، خصوصا في كود الرسومات وPDF المضبوط لمترجمات أقدم.
|
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; |
ينهار هذا الأسلوب عندما تترجم الوحدة نفسها لهدف 64 بت. يعرض مترجم Delphi ذي 64 بت الخطأ E1025 Unsupported language feature: 'ASM' لأن DCC64 لا يدعم خلط Pascal وinline assembly داخل روتينات Pascal العادية.
لماذا يرفضه مترجم 64 بت
يستطيع DCC64 تجميع روتينات assembly ذات 64 بت، لكن يجب كتابة الروتين كروتين assembler كامل مع مراعاة اتفاقية الاستدعاء ذات 64 بت. لا يمكنك نسخ inline assembler من 32 بت إلى جسم دالة Pascal وتوقع أن يترجم المترجم استخدام السجلات أو تخطيط stack أو سلوك x87.
يمكن إنشاء نسخة assembler كاملة، والكود التالي يوضح نوع معالجة control-word الذي قد يلزم عندما تكون القيمة موجودة بالفعل على FPU stack.
|
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; |
فضل Pascal عندما يكون الهدف هو القطع فقط
في معظم كود التطبيقات، لا حاجة إلى روتين assembler. إذا كان المتطلب الحقيقي هو قطع Double نحو الصفر وإرجاع قيمة عدد صحيح، فالنسخة الواضحة والقابلة للنقل هي ببساطة:
|
1 2 3 4 |
function _ftol(f: double): integer; cdecl; begin Result := Trunc(f); end; |
Trunc يعبر عن القصد مباشرة، ويترجم لأهداف 32 بت و64 بت، ويتجنب تكلفة صيانة assembler خاص بالمعمارية. كما أنه أسهل على الصيانة المستقبلية عند التفكير في فحص النطاق وسلوك overflow.
إرشادات الترحيل
- استخدم تنفيذ Pascal عندما تتحكم في موضع الاستدعاء وتحتاج فقط إلى تحويل رقمي.
- احتفظ بتصدير assembler فقط عندما تتطلب التوافقية الثنائية رمزا واتفاقية استدعاء محددين.
- لا تنسخ كود سجلات 32 بت إلى روتين 64 بت دون مراجعة اتفاقية الاستدعاء وعرض البيانات.
- اختبر حالات الحافة مثل القيم السالبة، والقيم الكبيرة، والقيم خارج نطاق العدد الصحيح الهدف.
اعتبارات النطاق والسلوك
يجب اعتبار استبدال assembler بـ Trunc تغييرا في السلوك يستحق الاختبارات. أكد كيف يجب أن يتصرف الكود مع الأرقام السالبة، والكسور القريبة من حد عدد صحيح، والقيم خارج نطاق Integer. ربما اعتمد assembler القديم على حالة FPU control-word، بينما تتبع نسخة Pascal تنفيذ RTL للمترجم الهدف.
إذا كان المساعد داخل مكون PDF أو رسومات، فاختبر المخرجات التي تستخدم نصا مدورا، وإحداثيات مكبرة، وأوامر رسم محولة. هذه المناطق أقدر على كشف فروق تحويل float-to-integer من الأمثلة البسيطة.
مسار الترحيل الموصى به
أولا، استبدل مساعدات التحويل الخاصة بكود Pascal واضح حيثما أمكن. ثانيا، احتفظ بالتنفيذ الأصلي 32 بت فقط خلف شرط 32 بت إذا كان لا يزال مطلوبا. ثالثا، أضف فرعا منفصلا 64 بت فقط عندما يبرر ذلك قياس أداء أو متطلب توافقية ثنائية. هذا يبقي المسار القابل للنقل بسيطا ويجعل المسار الخاص بالمعمارية استثناء لا قاعدة.