2

Delphi BASM32 コードを FPC に移植しているときに問題が発生しました。

program MulTest;

{$IFDEF FPC}
  {$mode delphi}
  {$asmmode intel}
{$ELSE}
  {$APPTYPE CONSOLE}
{$ENDIF}

function Mul(A, B: LongWord): LongWord;
asm
         MUL    EAX,EDX
end;

begin
  Writeln(Mul(10,20));
  Readln;
end.

上記のコードは Delphi XE でコンパイルされ、期待どおりに動作します。MUL EAX,EDXFPC は次の行にコンパイル時エラーを出力します。

エラー: Asm: [mul reg32,reg32] オペコードとオペランドの無効な組み合わせ

Win32 用の Lazarus 1.4.4/FPC2.6.4 (現在の安定版) を使用しています。

問題の回避策または修正はありますか?

4

2 に答える 2

10

FreePascal は正しいです。には 3 つの形式しかありませんMUL

MUL r/m8
MUL r/m16
MUL r/m32

第 1 オペランド (デスティネーション オペランド) と第 2 オペランド (ソース オペランド) の符号なし乗算を実行し、結果をデスティネーション オペランドに格納します。デスティネーション オペランドは、レジスタ AL、AX、または EAX (オペランドのサイズに応じて) にある暗黙のオペランドです。ソース オペランドは、汎用レジスタまたはメモリ ロケーションにあります。

つまり、第 1 オペランド (入力と出力の両方に使用される) はAL/ AX/EAXで指定され、第 2 オペランドは汎用レジスタまたはメモリ アドレスとして明示的に指定されます。

したがって、MUL EAX,EDX実際には無効なアセンブリ命令です。

このコードを Delphi でコンパイルし、デバッガを使用して生成されたアセンブリを確認すると、 の呼び出しMul(10,20)によって次のアセンブリ コードが生成されることがわかります。

// Mul(10,20)
mov edx,$00000014
mov eax,$0000000a
call Mul

//MUL    EAX,EDX
mul edx

ご覧のように、Delphi は実際にソース コードを解析し、最初のオペランドが存在することを確認EAXしてそれを取り除き、正しいアセンブリを生成します。FreePascal はそのステップを行っていません。

回避策は?まず、適切なアセンブリ コードを記述します。コードの再解釈をコンパイラに頼らないでください。

function Mul(A, B: LongWord): LongWord;
asm
         MUL    EDX
end;

または、アセンブリ コードを直接記述できず、コンパイラに作業を任せることもできます。LongWord2 つの値を乗算する方法を知っています。

function Mul(A, B: LongWord): LongWord;
begin
  Result := A * B;
end;

この場合、 Delphi はIMUL代わりに ofを使用しますが。MULDelphiのドキュメントから:

の値は、とx / yの型Extendedに関係なく、型です。他の算術演算子の場合、少なくとも 1 つのオペランドが実数の場合、結果は型になります。それ以外の場合、少なくとも 1 つのオペランドが type の場合、結果はtypeです。それ以外の場合、結果の型はです。オペランドの型が整数型の部分範囲である場合、それは整数型であるかのように扱われます。xyExtendedInt64Int64Integer

また、スタックフレームを無効にして最適化を有効にしない限り、見苦しい肥大化したアセンブリを使用します。Mul()これらの 2 つのオプションを構成することにより、単一IMUL EDXの命令 (RETもちろん、命令も)を生成することができます。プロジェクト全体でオプションを変更したくない場合は、 /および/コンパイラ命令Mul()を使用するだけでそれらを分離できます。{$STACKFRAMES OFF}{$W-}{$OPTIMIZATION ON}{$O+}

{$IFOPT W+}{$W-}{$DEFINE SF_Was_On}{$ENDIF}
{$IFOPT O-}{$O+}{$DEFINE O_Was_Off}{$ENDIF}
function Mul(A, B: LongWord): LongWord;
begin
  Result := A * B;
end;
{$IFDEF SF_Was_On}{W+}{$UNDEF SF_Was_On}{$ENDIF}
{$IFDEF O_Was_Off}{O-}{$UNDEF O_Was_Off}{$ENDIF}

生成:

imul edx
ret
于 2015-12-30T06:27:05.570 に答える
6

MUL always multiplies by AL, AX or EAX (more details), so you should specify only the other operand.

于 2015-12-30T06:26:51.863 に答える