简体   繁体   English

FPC BASM32 MUL bug?

[英]FPC BASM32 MUL bug?

I come across a problem while porting Delphi BASM32 code to FPC: 我在将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.

The above code compiles in Delphi XE and works as expected; 上面的代码在Delphi XE中编译并按预期工作; FPC outputs compile-time error on MUL EAX,EDX line: FPC在MUL EAX,EDX线上输出编译时错误:

Error: Asm: [mul reg32,reg32] invalid combination of opcode and operands 错误:Asm:[mul reg32,reg32]操作码和操作数的无效组合

I am using Lazarus 1.4.4/FPC2.6.4 for Win32 (the current stable version) 我正在使用Lazarus 1.4.4 / FPC2.6.4 for Win32(当前稳定版)

Any workaround or fix for the problem? 任何解决方法或修复问题?

FreePascal is correct. FreePascal是正确的。 There are only 3 forms of MUL : MUL只有3种形式:

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

Performs an unsigned multiplication of the first operand (destination operand) and the second operand (source operand) and stores the result in the destination operand. 执行第一个操作数(目标操作数)和第二个操作数(源操作数)的无符号乘法,并将结果存储在目标操作数中。 The destination operand is an implied operand located in register AL, AX or EAX (depending on the size of the operand); 目标操作数是位于寄存器AL,AX或EAX中的隐含操作数(取决于操作数的大小); the source operand is located in a general-purpose register or a memory location . 源操作数位于通用寄存器或存储器位置

In other words, the first operand (used for both input and output) is specified in AL / AX / EAX , and the second input operand is explicitly specified as either a general-purpose register or a memory address. 换句话说,第一个操作数(用于输入和输出)在AL / AX / EAX指定,第二个输入操作数显式指定为通用寄存器或存储器地址。

So, MUL EAX,EDX is indeed an invalid assembly instruction. 所以, MUL EAX,EDX确实是一个无效的汇编指令。

If you compile this code in Delphi and use the debugger to look at the generated assembly, you would see that the call to Mul(10,20) generates the following assembly code: 如果您在Delphi中编译此代码并使用调试器查看生成的程序集,您会看到对Mul(10,20)的调用生成以下汇编代码:

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

//MUL    EAX,EDX
mul edx

So, as you can see, Delphi is actual parsing your source code, sees that the first operand is EAX and strips it off for you, thus producing the correct assembly. 因此,正如您所看到的,Delphi实际上正在解析您的源代码,看到第一个操作数是EAX并为您剥离它,从而产生正确的程序集。 FreePascal is not doing that step for you. FreePascal并没有为你做那一步。

The workaround? 解决方法? Write proper assembly code to begin with. 编写正确的汇编代码。 Don't rely on the compiler to re-interpret your code for you. 不要依赖编译器为您重新解释代码。

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

Or, you could simply not write assembly code directly, let the compiler do the work for you. 或者,您可以直接编写汇编代码,让编译器为您完成工作。 It knows how to multiple two LongWord values together: 它知道如何将两个LongWord值组合在一起:

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

Though Delphi does use IMUL instead of MUL in this case. 虽然在这种情况下Delphi确实使用IMUL而不是MUL From Delphi's documentation : 来自Delphi的文档

The value of x / y is of type Extended , regardless of the types of x and y . 无论xy的类型如何, x / y的值都是Extended类型。 For other arithmetic operators, the result is of type Extended whenever at least one operand is a real; 对于其他算术运算符,只要至少一个操作数是实数,结果就是Extended类型; otherwise, the result is of type Int64 when at least one operand is of type Int64 ; 否则,当至少一个操作数是Int64类型时,结果是Int64类型; otherwise, the result is of type Integer . 否则,结果是Integer类型 If an operand's type is a subrange of an integer type, it is treated as if it were of the integer type. 如果操作数的类型是整数类型的子范围,则将其视为整数类型。

It also uses some unsightly bloated assembly unless stackframes are disabled and optimizations are enabled. 除非禁用堆栈帧并启用优化,否则它还会使用一些难看的膨胀组件。 By configuring those two options, it is possible to get Mul() to generate a single IMUL EDX instruction (plus the RET instruction, of course). 通过配置这两个选项,可以使Mul()生成单个IMUL EDX指令(当然还有RET指令)。 If you don't want to change the options project-wide, you can isolate them to just Mul() by using the {$STACKFRAMES OFF} / {$W-} and {$OPTIMIZATION ON} / {$O+} compiler instructions. 如果您不想在项目范围内更改选项,可以使用{$STACKFRAMES OFF} / {$W-}{$OPTIMIZATION ON} / {$O+}编译器指令将它们隔离到Mul()

{$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}

Generates: 产生:

imul edx
ret

MUL总是乘以AL,AX或EAX( 更多细节 ),因此您应该只指定另一个操作数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM