为什么在优化构建下有两个顺序移动到EAX?

时间:2017-07-26 16:35:35

标签: delphi assembly optimization delphi-10.2-tokyo

我查看了发布所有优化的发布版本的ASM代码,这是我遇到的内联函数之一:

0061F854 mov eax,[$00630bec]
0061F859 mov eax,[$00630e3c]
0061F85E mov edx,$00000001
0061F863 mov eax,[eax+edx*4]
0061F866 cmp byte ptr [eax],$01
0061F869 jnz $0061fa83

代码很容易理解,它将偏移量(1)构建到表中,将其中的字节值与1进行比较,如果是NZ则进行跳转。我知道我的表的指针存储在$ 00630e3c中,但我不知道$ 00630bec来自哪里。

为什么有两个人一个接一个地转向eax?不是第一个被第二个覆盖的第一个吗?这可能是缓存优化的事情还是我错过了令人难以置信的明显/模糊的东西?

上述ASM的Delphi代码如下:

if( TGameSignals.IsSet( EmitParticleSignal ) = True ) then [...]

IsSet()是一个内联类函数,并调用TSignalManager的内联IsSet()函数:

class function TGameSignals.IsSet(Signal: PBucketSignal): Boolean;
begin
  Result := FSignalManagerInstance.IsSet( Signal );
end;

信号管理器的最终IsSet如下:

function TSignalManagerInstance.IsSet( Signal: PBucketSignal ): Boolean;
begin
  Result := Signal.Pending;
end;

1 个答案:

答案 0 :(得分:9)

我最好的猜测是$ 00630bec是对TGameSignals类的引用。您可以通过

进行检查
ShowMessage(IntToHex(NativeInt(TGameSignals), 8))

预优化代码可能是这样的

0061F854 mov eax,[$00630bec] //Move reference to class TGameSignals in EAX
0061F859 mov eax,[eax + $250] //Move Reference to FSignalManagerInstance at offset $250 in class TGameSignals in EAX

编译器优化[eax + $250][$00630e3c],但没有意识到以前的MOV不再需要了。

我不是codegen的专家,所以带上一粒盐......

另外,在delphi中,我们通常会写

if TGameSignals.IsSet( EmitParticleSignal ) then

因为以下IF可能是真的

var vBool : Boolean
[...]
vBool := Boolean(10);
if vBool and (vBool <> True) then

当然,这不是一个好习惯,但与TRUE比较也没有意义。

编辑:正如Ped7g所指出的,我错了。指令是

0061F854 mov eax,[$00630bec] 

而不是

0061F854 mov eax,$00630bec

所以我写的内容并没有多大意义...... 第一条MOV指令用于将呼叫的“自我”引用传递给TGameSignals.IsSet。现在,如果函数不是内联函数,它将如下所示:

mov eax,[$00630bec]
call TGameSignals.IsSet

然后

*TGameSignals.IsSet
mov eax,[$00630e3c]
[...]

第一个mov仍然没有意义,因为TGameSignals.IsSet中没有使用“Self”,但仍需要将“self”传递给函数。当例程内联时,确实看起来更傻了。

如Arnaud Bouchez所述,使TGameSignals.IsSet静态删除隐式Self参数,从而删除第一个MOV操作。