Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are the 64-bit types and operations implemented on the lowest level on a 32-bit architecture?

Tags:

c++

How are types like int64_t implemented on the lowest i.e. assembly level? I'm using a 32 bit machine and still can use int64_t for example. My initial assumption is that the 64 bit are just simulated and thus there must be quite some overhead for computation with these types in comparison with 32 bit data types when being on a 32 bit machine.

Thank you in advance and Regards

like image 690
ben Avatar asked Mar 22 '23 05:03

ben


1 Answers

You are right, when you compile code for 32 bit architectures, you have to simulate 64 bit operands and operations using 32 bit operands.

An 8-byte variable (uint64_t which is just a typedef for long long) is stored in 2 4-byte registers.

For adding (and subtracting), you have to first add the lower 4 bytes and then perform a second add with carry (or a subtract with borrow) on the higher 4 bytes. Since the second add also adds the carry from the first add, the result is correct. The overhead for adding and subtracting is not much.

For multiplying and division however, things aren't that simple. Usually a routine is called to perform these kind of operations, and the overhead is significantly bigger.


Lets take this simple c code:

int main() {
  long long a = 0x0102030405060708;
  long long b = 0xA1A2A3A4A5A6A7A8;
  long long c = 0xB1B2B3B4B5B6B7B8;

  c = a + b;
  c = a - b;
  c = a * b;
  c = a / b;

  return 0;
}

Analyzing the assembly generated by MSVC we can see:


     2:   long long a = 0x0102030405060708;
012D13DE  mov         dword ptr [a],5060708h  
012D13E5  mov         dword ptr [ebp-8],1020304h  
     3:   long long b = 0xA1A2A3A4A5A6A7A8;
012D13EC  mov         dword ptr [b],0A5A6A7A8h  
012D13F3  mov         dword ptr [ebp-18h],0A1A2A3A4h  
     4:   long long c = 0xB1B2B3B4B5B6B7B8;
012D13FA  mov         dword ptr [c],0B5B6B7B8h  
012D1401  mov         dword ptr [ebp-28h],0B1B2B3B4h  

a 64 bit variable is split in 2 32-bit locations.


     6:   c = a + b;
012D1408  mov         eax,dword ptr [a]  
012D140B  add         eax,dword ptr [b]  
012D140E  mov         ecx,dword ptr [ebp-8]  
012D1411  adc         ecx,dword ptr [ebp-18h]  
012D1414  mov         dword ptr [c],eax  
012D1417  mov         dword ptr [ebp-28h],ecx  
     7:   c = a - b;
012D141A  mov         eax,dword ptr [a]  
012D141D  sub         eax,dword ptr [b]  
012D1420  mov         ecx,dword ptr [ebp-8]  
012D1423  sbb         ecx,dword ptr [ebp-18h]  
012D1426  mov         dword ptr [c],eax  
012D1429  mov         dword ptr [ebp-28h],ecx  

a sum is performed with an add instruction on the lower 32 bits and then with an adc (add with carry) for the higher 32 bits. Subtraction is similar: the second operation is sbb (subtract with borrow).


     8:   c = a * b;
012D142C  mov         eax,dword ptr [ebp-18h]  
012D142F  push        eax  
012D1430  mov         ecx,dword ptr [b]  
012D1433  push        ecx  
012D1434  mov         edx,dword ptr [ebp-8]  
012D1437  push        edx  
012D1438  mov         eax,dword ptr [a]  
012D143B  push        eax  
012D143C  call        __allmul (012D105Ah)  
012D1441  mov         dword ptr [c],eax  
012D1444  mov         dword ptr [ebp-28h],edx  
     9:   c = a / b;
012D1447  mov         eax,dword ptr [ebp-18h]  
012D144A  push        eax  
012D144B  mov         ecx,dword ptr [b]  
012D144E  push        ecx  
012D144F  mov         edx,dword ptr [ebp-8]  
012D1452  push        edx  
012D1453  mov         eax,dword ptr [a]  
012D1456  push        eax  
012D1457  call        __alldiv (012D1078h)  
012D145C  mov         dword ptr [c],eax  
012D145F  mov         dword ptr [ebp-28h],edx  

The product and division are performed by calling special routines.

like image 110
bolov Avatar answered Apr 08 '23 05:04

bolov