为什么在优化构建下有两次连续移动到 EAX?
Why is there two sequential move to EAX under optimization build?
我查看了启用了所有优化的发布版本的 ASM 代码,这是我遇到的内联函数之一:
0061F854 mov eax,[[=10=]630bec]
0061F859 mov eax,[[=10=]630e3c]
0061F85E mov edx,[=10=]000001
0061F863 mov eax,[eax+edx*4]
0061F866 cmp byte ptr [eax],
0061F869 jnz [=10=]61fa83
代码很容易理解,它将偏移量 (1) 构建到 table,将其字节值与 1 进行比较,如果 NZ 则跳转。我知道指向我的 table 的指针存储在 $00630e3c 中,但我不知道 $00630bec 是从哪里来的。
为什么要两步走一个接一个?第一个不是被第二个覆盖了吗?这可能是缓存优化问题还是我遗漏了令人难以置信的东西 obvious/obscure?
上述ASM的Delphi代码如下:
if( TGameSignals.IsSet( EmitParticleSignal ) = True ) then [...]
IsSet()是一个内联的class函数,调用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;
我最好的猜测是 $00630bec 是对 class TGameSignals 的引用。您可以通过
来检查它
ShowMessage(IntToHex(NativeInt(TGameSignals), 8))
优化前的代码大概是这样的
0061F854 mov eax,[[=11=]630bec] //Move reference to class TGameSignals in EAX
0061F859 mov eax,[eax + 0] //Move Reference to FSignalManagerInstance at offset 0 in class TGameSignals in EAX
编译器将[eax + 0]
优化为[[=19=]630e3c]
,但没有意识到不再需要之前的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,[[=14=]630bec]
而不是
0061F854 mov eax,[=15=]630bec
所以我写的东西真的没有意义......
第一条 MOV 指令用于传递 "self" 引用以调用 TGameSignals.IsSet
。现在,如果函数不是内联的,它看起来像这样:
mov eax,[[=16=]630bec]
call TGameSignals.IsSet
然后
*TGameSignals.IsSet
mov eax,[[=17=]630e3c]
[...]
第一个 mov 仍然没有意义,因为 TGameSignals.IsSet
中没有使用 "Self" 但仍然需要将 "self" 传递给函数。当例程被内联时,它看起来确实更傻了。
正如 Arnaud Bouchez 所提到的,使 TGameSignals.IsSet
static 删除隐式的 Self 参数,从而删除第一个 MOV 操作。
我查看了启用了所有优化的发布版本的 ASM 代码,这是我遇到的内联函数之一:
0061F854 mov eax,[[=10=]630bec]
0061F859 mov eax,[[=10=]630e3c]
0061F85E mov edx,[=10=]000001
0061F863 mov eax,[eax+edx*4]
0061F866 cmp byte ptr [eax],
0061F869 jnz [=10=]61fa83
代码很容易理解,它将偏移量 (1) 构建到 table,将其字节值与 1 进行比较,如果 NZ 则跳转。我知道指向我的 table 的指针存储在 $00630e3c 中,但我不知道 $00630bec 是从哪里来的。
为什么要两步走一个接一个?第一个不是被第二个覆盖了吗?这可能是缓存优化问题还是我遗漏了令人难以置信的东西 obvious/obscure?
上述ASM的Delphi代码如下:
if( TGameSignals.IsSet( EmitParticleSignal ) = True ) then [...]
IsSet()是一个内联的class函数,调用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;
我最好的猜测是 $00630bec 是对 class TGameSignals 的引用。您可以通过
来检查它ShowMessage(IntToHex(NativeInt(TGameSignals), 8))
优化前的代码大概是这样的
0061F854 mov eax,[[=11=]630bec] //Move reference to class TGameSignals in EAX
0061F859 mov eax,[eax + 0] //Move Reference to FSignalManagerInstance at offset 0 in class TGameSignals in EAX
编译器将[eax + 0]
优化为[[=19=]630e3c]
,但没有意识到不再需要之前的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,[[=14=]630bec]
而不是
0061F854 mov eax,[=15=]630bec
所以我写的东西真的没有意义......
第一条 MOV 指令用于传递 "self" 引用以调用 TGameSignals.IsSet
。现在,如果函数不是内联的,它看起来像这样:
mov eax,[[=16=]630bec]
call TGameSignals.IsSet
然后
*TGameSignals.IsSet
mov eax,[[=17=]630e3c]
[...]
第一个 mov 仍然没有意义,因为 TGameSignals.IsSet
中没有使用 "Self" 但仍然需要将 "self" 传递给函数。当例程被内联时,它看起来确实更傻了。
正如 Arnaud Bouchez 所提到的,使 TGameSignals.IsSet
static 删除隐式的 Self 参数,从而删除第一个 MOV 操作。