Reverse Engineering for Beginners

(avery) #1

CHAPTER 27. WORKING WITH FLOATING POINT NUMBERS USING SIMD CHAPTER 27. WORKING WITH FLOATING POINT NUMBERS USING SIMD


27.2 Passing floating point number via arguments.


#include <math.h>
#include <stdio.h>


int main ()
{
printf ("32.01 ^ 1.54 = %lf\n", pow (32.01,1.54));


return 0;
}


They are passed in the lower halves of theXMM0-XMM3registers.


Listing 27.5: Optimizing MSVC 2012 x64

$SG1354 DB '32.01 ^ 1.54 = %lf', 0aH, 00H


real@40400147ae147ae1 DQ 040400147ae147ae1r ; 32.01
real@3ff8a3d70a3d70a4 DQ 03ff8a3d70a3d70a4r ; 1.54


main PROC
sub rsp, 40 ; 00000028H
movsdx xmm1, QWORD PTR real@3ff8a3d70a3d70a4
movsdx xmm0, QWORD PTR
real@40400147ae147ae1
call pow
lea rcx, OFFSET FLAT:$SG1354
movaps xmm1, xmm0
movd rdx, xmm1
call printf
xor eax, eax
add rsp, 40 ; 00000028H
ret 0
main ENDP


There is noMOVSDXinstruction in Intel [Int13] and AMD [AMD13a] manuals, there it is called justMOVSD. So there are two
instructions sharing the same name in x86 (about the other see:A.6.2 on page 887). Apparently, Microsoft developers wanted
to get rid of the mess, so they renamed it toMOVSDX. It just loads a value into the lower half of a XMM register.


pow()takes arguments fromXMM0andXMM1, and returns result inXMM0. It is then moved toRDXforprintf(). Why?
Maybe becauseprintf()—is a variable arguments function?


Listing 27.6: Optimizing GCC 4.4.6 x64

.LC2:
.string "32.01 ^ 1.54 = %lf\n"
main:
sub rsp, 8
movsd xmm1, QWORD PTR .LC0[rip]
movsd xmm0, QWORD PTR .LC1[rip]
call pow
; result is now in XMM0
mov edi, OFFSET FLAT:.LC2
mov eax, 1 ; number of vector registers passed
call printf
xor eax, eax
add rsp, 8
ret
.LC0:
.long 171798692
.long 1073259479
.LC1:
.long 2920577761
.long 1077936455


GCC generates clearer output. The value forprintf()is passed inXMM0. By the way, here is a case when 1 is written into
EAXforprintf()—this implies that one argument will be passed in vector registers, just as the standard requires [Mit13].

Free download pdf