1

DelphiでASMをいじっています。私の理解では、EAX は Result を保持しています。以下では、最後に RET を付ける必要があります。それ以外の場合、Result は正しくありません (入力が 0 の場合は正しい)。私は何を間違っているのですか、それとも何を理解していないのですか?

function MSb(const Val: Integer): Integer;
label
  Go;
asm
  CMP       EAX, 0
  JNZ       Go
  MOV       EAX, -1
  RET
Go:
  BSR       EBX, EAX
  MOV       EAX, EBX
  RET
end;

私なら次のように言います。

  MOV       Result, EBX

次に、次のコンパイルを取得します。

  MOV [EPB-$04], EBX
  MOV EAX, [EPB-$04]

ただし、上記のコードには次の追記があります。

  MOV       EAX, EDX
  RET
4

2 に答える 2

4

少なくとも最適化が有効になっていない場合、関数にはプリアンブルとポストアンブルがあります。それを見てください:

Project46.dpr.13: asm
0041A1F4 55プッシュebp
0041A1F5 8BEC mov ebp,esp
0041A1F7 83C4F8 追加 esp、-$08
0041A1FA 8945F8 mov [ebp-$08],eax
Project46.dpr.14: CMP EAX、0
0041A1FD 83F800 cmp eax,$00
Project46.dpr.15: JNZ 囲碁
0041A200 7506 jnz $0041a208
Project46.dpr.16: MOV EAX、-1
0041A202 B8FFFFFFFF mov eax,$ffffffff
Project46.dpr.17: RET
0041A207 C3 ret
Project46.dpr.19: BSR EBX、EAX
0041A208 0FBDD8 bsr ebx,eax
Project46.dpr.20: MOV EAX、EBX
0041A20B 89D8 mov eax,ebx
Project46.dpr.21: RET
0041A20D C3 ret
Project46.dpr.22: 終わり;
0041A20E 8B45FC mov eax,[ebp-$04]
0041A211 59 ポップ ecx
0041A212 59 ポップ ecx
0041A213 5D ポップ ebp
0041A214 C3 ret

したがって、プリアンブルはスタック フレームを設定します。レジスタを保存し、レジスタとレジスタebpの両方を変更します。ポストアンブルにも注意してください。保存されたレジスタを復元するには、そのコードを実行する必要があります。ebpesp

これに対処する通常の方法は、 を使用する代わりに関数の最後にジャンプすることretです。したがって、次のようにコードを記述します。

function MSb(const Val: Integer): Integer;
asm
  CMP       EAX, 0
  JNZ       @@go
  MOV       EAX, -1
  JMP       @@exit
@@go:
  BSR       EBX, EAX
  MOV       EAX, EBX
@@exit:
end;

このようにして、ポストアンブルが確実に実行されるようにします。プリアンブルが確実に実行されるようにするには、この方法でコードを記述する習慣を身に付ける必要があります。


さて、それ以上に、質問で言及した問題は、実際には asm ラベルではなく Pascal ラベルの使用に関連するコンパイラのバグに関連していると思われます。まあ、これはコンパイラのバグかもしれませんが、Pascal ラベルを使用するのは単なる間違いかもしれません。次のプログラムを検討してください。

{$APPTYPE CONSOLE}

function MSb(const Val: Integer): Integer;
asm
  CMP       EAX, 0
  JNZ       @@Go
  MOV       EAX, -1
  JMP       @@exit
@@Go:
  BSR       EBX, EAX
  MOV       EAX, EBX
@@exit:
end;

function MSb2(const Val: Integer): Integer;
label
  Go;
asm
  CMP       EAX, 0
  JNZ       Go
  MOV       EAX, -1
  RET
Go:
  BSR       EBX, EAX
  MOV       EAX, EBX
end;

begin
  Writeln(Msb(0));
  Writeln(Msb(1));
  Writeln(Msb2(0));
  Writeln(Msb2(1));
  Readln;
end.

最適化してコンパイルした場合の出力は次のとおりです。

-1
0
-1
4

それで、それはどうですか4Msb2さて、本質的にあなたのコードである のアセンブルされたコードを見てみましょう:

004059E8 83F800 cmp eax,$00
004059EB 7506 jnz $004059f3
004059ED B8FFFFFFFF mov eax,$ffffffff
004059F2 C3 ret
004059F3 0FBDD8 bsr ebx,eax
004059F6 89D8 mov eax,ebx
004059F8 8BC2 mov eax,edx
004059FA C3 ret

いったいなぜ、値edxが割り当てられていない揮発性レジスタの値がeax、関数が戻る直前に移動されるのですか。これがあなたが報告している問題です。私の推測では、Pascal ラベルの使用がアセンブラを混乱させているようです。asm ラベルに固執します。

のアセンブルされたコードは次のMsbとおりです。

004059D4 83F800 cmp eax,$00
004059D7 7506 jnz $004059df
004059D9 B8FFFFFFFF mov eax,$ffffffff
004059DE C3 ret
004059DF 0FBDD8 bsr ebx,eax
004059E2 89D8 mov eax,ebx
004059E4 C3 ret

そのほうが似てる!ここに post-able がないことをコンパイラがどのように認識しているかに注意してjmp @@exitくださいret

于 2014-11-25T18:00:02.727 に答える
1

私は何を間違っているのですか、それとも何を理解していないのですか?

基本的にプリアンブルとポストアンブルは明確に述べられていますが、アセンブラ コードを使用する場合も注意が必要です。パラメータは、呼び出し規約に応じてさまざまな方法で渡されます。Pascalを削除しlabelRETアセンブラ ラベル ( ) を使用すると、コードは (皮肉なことに廃止された) 呼び出し規約で正常に実行されます@@。アセンブリ コードの呼び出し規則を常に示すことをお勧めします。これは、アセンブリ コードを保持するローカル変数をResult実際に参照する場合があるためです。(デフォルト) 呼び出し規約を使用する場合は、 に直接マップされます。EAXEAXResultRegister

于 2014-11-25T22:05:15.713 に答える