Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there two sequential move to EAX under optimization build?

I looked at the ASM code of a release build with all optimizations turned on, and here is one of the inlined function I came across:

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

The code is pretty easy to understand, it builds an offset (1) into a table, compares the byte value from it to 1 and do a jump if NZ. I know the pointer to my table is stored in $00630e3c, but I have no idea where $00630bec is coming from.

Why is there two move to eax one after the other? Isn't the first one overwritten by the second one? Can this be a cache optimization thing or am I missing something unbelievably obvious/obscure?

The Delphi code for the above ASM is as follow:

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

IsSet() is an inlined class function and calls the inlined IsSet() function of TSignalManager:

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

The final IsSet of the signal manager is as such:

function TSignalManagerInstance.IsSet( Signal: PBucketSignal ): Boolean;
begin
  Result := Signal.Pending;
end;
like image 896
Eric Fortier Avatar asked Jul 26 '17 16:07

Eric Fortier


1 Answers

My best guess would be that $00630bec is a reference to the class TGameSignals. You can check it by doing

ShowMessage(IntToHex(NativeInt(TGameSignals), 8))

The pre-optimisation code was probably something like this

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

the compiler optimised [eax + $250] to [$00630e3c], but didn't realize the previous MOV wasn't required anymore.

I'm not an expert in codegen, so take it with a grain of salt...

On a side note, in delphi, we usually write

if TGameSignals.IsSet( EmitParticleSignal ) then

As it's possible for the following IF to be true

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

Granted, this is not good practice, but no point in comparing to TRUE either.

EDIT: As pointed out by Ped7g, I was wrong. The instruction is

0061F854 mov eax,[$00630bec] 

and not

0061F854 mov eax,$00630bec

So what I wrote didn't really make sense... The first MOV instruction serve to pass the "self" reference for the call to TGameSignals.IsSet. Now, if the function wasn't inline, it would look like this :

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

and then

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

The first mov is still pointless, since "Self" isn't used in TGameSignals.IsSet but it is still required to pass "self" to the function. When the routine get inlined, it looks a lot more silly, indeed.

Like mentioned by Arnaud Bouchez, making TGameSignals.IsSet static remove the implicit Self parameter and thus, remove the first MOV operation.

like image 200
Ken Bourassa Avatar answered Sep 25 '22 23:09

Ken Bourassa