Delphi Label و ASM الغرابة؟
سؤال
لقد كتبت وظيفة ASM في Delphi 7 لكنها تحول الكود إلى شيء آخر:
function f(x: Cardinal): Cardinal; register;
label err;
asm
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz err
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
err:
xor eax, eax
end;
// compiled version
f:
push ebx // !!!
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz +$0e
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
err:
xor eax, eax
mov eax, ebx // !!!
pop ebx // !!!
ret
// the almost equivalent without asm
function f(x: Cardinal): Cardinal;
var
c: Cardinal;
begin
x := not x;
x := x and x shr 1;
if x <> 0 then
begin
c := bsf(x); // bitscanforward
x := 1 shl c;
Result := x or (x shl 1)
end
else
Result := 0;
end;
لماذا يولد push ebx
و pop ebx
؟ ولماذا تفعل mov eax, ebx
?
يبدو أنه يولد إطار المكدس الجزئي بسبب mov eax, ebx
.
يولد هذا الاختبار البسيط mov eax, edx
لكن لا يولد إطار المكدس هذا:
function asmtest(x: Cardinal): Cardinal; register;
label err;
asm
not eax
and eax, 1
jz err
ret
err:
xor eax, eax
end;
// compiled
asmtest:
not eax
and eax, $01
jz +$01
ret
xor eax, eax
mov eax, edx // !!!
ret
يبدو أن له علاقة مع label err
. إذا قمت بإزالة أنني لا أحصل على mov eax, *
جزء.
لماذا يحدث هذا؟
قدم تقرير الأخطاء الجودة المركزية.
المحلول
النصيحة العملية هي: لا تستخدم الكلمات الرئيسية في رمز ASM ، استخدم الملصقات المسبقة:
function f(x: Cardinal): Cardinal; register;
asm
not eax
mov edx,eax
shr edx, 1
and eax, edx
bsf ecx, eax
jz @@err
mov eax, 1
shl eax, cl
mov edx, eax
add edx, edx
or eax, edx
ret
@@err:
xor eax, eax
end;
محدث:
لم أجد تقرير الأخطاء في منطقة BASM. يبدو الأمر وكأنه خطأ ، لكنني استخدمت BOSM لسنوات عديدة ولم أفكر أبدًا في استخدام Label Keyword بهذه الطريقة. في الواقع ، لم أستخدم كلمة التسمية في Delphi على الإطلاق. قون
نصائح أخرى
حسنًا ... في ذلك الوقت ، في Delphi Manual ، كان يقول شيئًا عن التحسين المترجم والتجمع المترجم:
يقوم المترجم بإنشاء StackFrames فقط للروتين المتداخل ، والروتين الذي يحتوي على متغيرات محلية وللإجراءات ذات المعلمات المعلمة
يشمل رمز التهيئة تلقائيًا- وتصفية الرمز للرقابة:
PUSH EBP ; If Locals <> 0 or Params <> 0
MOV EBP,ESP ; If Locals <> 0 or Params <> 0
SUB ESP,Locals ; If Locals <> 0
...
MOV ESP,EBP ; If Locals <> 0
POP EBP ; If Locals <> 0 or Params <> 0
RET Params ; Always
إذا كانت المتغيرات المحلية تحتوي على متغيرات أو سلاسل طويلة أو واجهات تتم تهيئتها باستخدام NULL ولكن لا يتم الانتهاء منها بعد ذلك.
السكان المحليين هو حجم المتغيرات المحلية ، والمعلمات حجم المعلمات. إذا كان كل من السكان المحليين وكذلك params فارغًا ، فلن يتم إنشاء رمز init وسيحتوي رمز اللمسات الأخيرة فقط على استنبط.
ربما يكون لهذا الأمر علاقة بكل شيء ...