2

I'm trying to convert some x86 assembly back into C++, and I cannot figure out how this set of instructions was originally written.

movd xmm0,eax ; byte read from device '0x04'
cvtdq2pd xmm0,xmm0 ; convert packed to double?
shr eax,0x1F ; highest bit
addsd xmm0,qword ptr ds:[eax*8+0x4F6CC0] ; global [0, 4294967296] if I read it right
cvtpd2ps xmm0,xmm0 ; double to packed?
movss dword ptr ds:[ebx+0x34],xmm0 ; store the result

I've tried various forms of casting float and double to other data types on Compiler Explorer but I cannot find anything that reproduces the cvtdq2pd and cvtpd2ps instructions.

What would the above code look like in c/c++ and what is the resulting data type?

  • 1
    I'd guess the first four lines are loading an unsigned 32-bit value into a double: if cvtdq2pd treats the input as signed then you'd need to add 1<<32 to correct the output if the top bit of the input is set. – Rup Sep 7 '18 at 0:41
8

You likely won't get an exact reproduction because cvtdq2pd takes the lower 64 bits of the second operand but since we're limited to 32 bits because we're using eax here, there are probably better(?) instructions to use.

cvtsi2sd xmm0, eax

will do the same thing as

movd xmm0,eax

cvtdq2pd xmm0,xmm0

See here https://www.felixcloutier.com/x86/CVTDQ2PD.html & https://www.felixcloutier.com/x86/CVTSI2SD.html

So really what it's doing is converting a 32 bit signed integer value into a double precision floating point.


Onto your actual question:

cvtpd2ps xmm0,xmm0 ; double to packed?

CVTPD2PS xmm1, xmm2/m128

Convert two packed double-precision floating-point values in xmm2/mem to two single-precision floating-point values in xmm1.

This will pack the two double precision floats at xmm0[0:63] & xmm0[64:127] into the lower 64 bits of xmm0, converting them from double to single precision floating point values (xmm0[0:31] & xmm0[32:63]).

Ref: https://www.felixcloutier.com/x86/CVTPD2PS.html

So if the lower 64 bits of xmm0 represented a double, it's now been converted to a 32 bit float, which now sits in the lower 32 bits of xmm0.

movss dword ptr ds:[ebx+0x34],xmm0

MOVSS xmm2/m32, xmm1

Move scalar single-precision floating-point value from xmm1 register to xmm2/m32.

Now stores the 4 byte result from xmm0[0:31] into [ebx+0x34], which we know is a single precision float from the result of the cvtpd2ps operation.

So the result of this operation is a 32 bit float.

Ref: https://www.felixcloutier.com/x86/MOVSS.html


This code here should be a reasonable approximation. In Godbolt it gives me similar assembly to what you have.

double* d_arr = (double*)0x4F6CC0;

int main() {

    int in = 4;
    int signbit = ((unsigned int)in >> 31);
    float result = *(double*)(d_arr + signbit) + in;
    return 0;
}

Conclusion:

cvtdq2pd & cvtpd2ps are too powerful for what's actually being calculated here. Unless I'm reading this totally wrong, the upper 64 bits of xmm0 are never relevant to the result.

Disclaimer:

I've never used floating point assembly before. I just looked up the docs now. I could be missing something.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.