سؤال

لقد كتبت وظيفة 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 وسيحتوي رمز اللمسات الأخيرة فقط على استنبط.


ربما يكون لهذا الأمر علاقة بكل شيء ...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top