5

Delphi 7 で asm 関数を作成しましたが、コードが別のものに変換されます。

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, *

なぜこれが起こるのですか?


Quality Centralでバグ レポートを作成しました。

4

2 に答える 2

7

The practical advice is: do not use label keyword in asm code, use @@-prefixed labels:

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;

Updated:

I have not found the bug report in Basm area. It looks like a bug, but I have used BASM for many years and never thought about using label keyword such a way. In fact I never used label keyword in Delphi at all. :)

于 2010-03-07T09:33:40.620 に答える
1

ええと...当時、Delphiマニュアルでは、コンパイラの最適化と同様の狂気について何かを言っていました:


コンパイラは、ネストされたルーチン、ローカル変数を持つルーチン、およびスタック パラメータを持つルーチンに対してのみスタックフレームを生成します。

ルーチンの自動生成された初期化コードと最終化コードには、次のものが含まれます。

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 で初期化されますが、後でファイナライズされません。

Locals はローカル変数のサイズ、Params はパラメーターのサイズです。Locals と Params の両方が Null の場合、Init-Code は生成されず、Finalizationcode には RET-Intruction のみが含まれます。


ひょっとしたら、それがすべてに関係しているのかもしれません...

于 2010-04-01T00:18:54.427 に答える