I have the following code:
public static float F(float n)
{
return -n;
}
which generates following asm:
Program.F(Single)
L0000: push eax
L0001: vzeroupper
L0004: vmovss xmm1, [esp+8]
L000a: vmovss xmm0, [Program.F(Single)] ; remember this line
L0012: vxorps xmm0, xmm0, xmm1 ; remember this line
L0016: vmovss [esp], xmm0
L001b: fld st, dword ptr [esp]
L001e: pop ecx
L001f: ret 4
on the other hand I have following snippet:
public static float G(float n)
{
return n * -1;
}
which generates:
Program.G(Single)
L0000: push eax
L0001: vzeroupper
L0004: vmovss xmm0, [esp+8]
L000a: vmulss xmm0, xmm0, [Program.G(Single)] ; remember this line
L0012: vmovss [esp], xmm0
L0017: fld st, dword ptr [esp]
L001a: pop ecx
L001b: ret 4
Questions
- As you may noticed the outputs are a little bit different. Why do we have 2 different assemblies? Do the codes not perform exactly the same task?
- Is there a reason for such big chunk of assembly? If yes why?
- How can I get more efficient version of it.
Note
The NOTEs are for the last question.
I want to get basically the "C" like assembly. E.g.:
float
f(float n) {
return -n;
}
float
g(float n) {
return n * -1;
}
which generates:
f:
xorps xmm0, XMMWORD PTR .LC0[rip]
ret
g:
xorps xmm0, XMMWORD PTR .LC0[rip]
ret
.LC0:
.long -2147483648
.long 0
.long 0
.long 0
- I tried following but it didn't work quite well:
public static unsafe float F(float n)
{
uint i = *(uint*)&n;
i ^= 0x80000000;
n = *(float*)&i;
return n;
}
I got the following which I think is even less efficient:
Program.F(Single)
L0000: sub esp, 8
L0003: vzeroupper
L0006: mov eax, [esp+0xc]
L000a: mov [esp], eax
L000d: mov eax, [esp]
L0010: xor eax, 0x80000000
L0015: mov [esp], eax
L0018: vmovss xmm0, [esp]
L001d: vmovss [esp+0xc], xmm0
L0023: vmovss xmm0, [esp+0xc]
L0029: vmovss [esp+4], xmm0
L002f: fld st, dword ptr [esp+4]
L0033: add esp, 8
L0036: ret 4
- I made a silly mistake (see the comment). Forgot to switch
x64mode:
Now we have an "efficient" assembly code:
public static float F(float n)
{
return -n;
}
public static float G(float n)
{
return n * -1;
}
C.F(Single)
L0000: vzeroupper
L0003: vmovss xmm1, [C.F(Single)]
L000b: vxorps xmm0, xmm0, xmm1
L000f: ret
C.G(Single)
L0000: vzeroupper
L0003: vmulss xmm0, xmm0, [C.G(Single)]
L000b: ret
BUT the 1st and 2nd question are valid I think:
Why do we have different ASM outputs here?