Tag: assembly

  • Population Count using popcnt instruction (counting number of on bits)

    Using popcnt instruction found on newer processors to get population count which is number of set bits.

    Reference : SSE4

    Here are couple example inputs with expected output.

     0xA =  1010 -- > 2
     0x3F2 =  1111110010 -- > 7	
    
    #include 
    #include 
    
    using namespace std;
    
    int main() {
    	// 0xA =  1010 -- > 2
    	// 0x3F2 =  1111110010 -- > 7
    	
    	unsigned long code = 0x3F2UL;
    	int bitcount = 0;
    
    	__asm__( "movl %1, %%eax; "  //code into eax
    			"popcnt  %%eax,  %%eax;"//  call popcnt instruction
    			"movl %%eax, %0;"// ebx into bitcount
    			:"=r"(bitcount)// output
    			:"r"(code)// input
    			:"%eax","%ebx","%ecx","%edx"// clobbered register
    	);
    
    	printf("%d", bitcount);
    	return 0;
    }
    

    Update
    To answer Phils question why we have "%eax","%ebx","%ecx","%edx" as our clobbered register list, this is my understanding and it could be completely wrong.

    Our instruction movl %%eax, %0; used GCC syntax for accessing c++ variable bitcount. So ‘%0’ refers to bitcount which will be copied into ebx register, that is why I have ebx in the list, as for the ecxand edx they have been added purely because they are general purpose registers and I did not want to override some previously stored value and my limited understanding on how inline assembly works.

  • Using ‘cpuid’ instructions to get cache line size

    This is program demonstrates how to use ‘cpuid’ assembly instruction on x86 to retrieve cache line size. This is done in c++ and it uses the inline assembly to execute the instructions, compiled with gcc on win32.

    Really we are only interested in Cache line information which can be found using 0x80000006 code and its lower 8 bits returned from ECX register.

    #include 
    #include 
    #include 
    
    int MASK_LSB_8 = 0xFF;
    
    void int2bin(int val) {
    	char buffer[33];
    	itoa(val, buffer, 2);
    	printf("binary: %s\n", buffer);
    }
    
    // CPUID instruction takes no parameters as CPUID implicitly uses the EAX register.
    // The EAX register should be loaded with a value specifying what information to return
    void cpuinfo(int code, int *eax, int *ebx, int *ecx, int *edx) {
    	__asm__ volatile(
    			"cpuid;" //  call cpuid instruction
    			:"=a"(*eax),"=b"(*ebx),"=c"(*ecx), "=d"(*edx)// output equal to "movl  %%eax %1"
    			:"a"(code)// input equal to "movl %1, %%eax"
    			//:"%eax","%ebx","%ecx","%edx"// clobbered register
    	);
    }
    
    int main() {
    	int eax = 0, ebx = 0, ecx = 0, edx = 0;
    	//CPUID(0): Basic information
    	cpuinfo(0x0, &eax, &ebx, &ecx, &edx);
    
    	// check vendor [GenuineIntel, GenuntelineI] = ebx+ecx+edx
    	char vendor[13] = { 0 };
    	int identity_reg[3] = { ebx, ecx, edx };
    	for (unsigned int i = 0; i < 3; i++) {
    		for (unsigned int j = 0; j < 4; j++) {
    			vendor[i * 4 + j] = identity_reg[i] >> (j * 8) & MASK_LSB_8;
    		}
    	}
    	printf("\nVendor name = %s\n", vendor);
    
    	//CPUID(0x80000000): Extended CPU information
    	cpuinfo(0x80000000, &eax, &ebx, &ecx, &edx);
    	int intelExtended = eax & MASK_LSB_8;
    	printf("\nMax feature id = %d\n", intelExtended);
    	if (intelExtended < 6) {
    		printf("\nCache Extended Feature not supported");
    		return 0;
    	}
    	// CPUID(0x800000006): Cache line information
    	cpuinfo(0x80000006, &eax, &ebx, &ecx, &edx);
    	// We are only interested in lower 8 bits
    	printf("\nL2 Cache Size = %d\n", (ecx & MASK_LSB_8));
    }
    

    Here is the output from my machine.

    Vendor name = GenuntelineI
    Max feature id = 8
    L2 Cache Size = 64
    

    The following result can be verified by using cpu-z program.

      	L1 Data cache	4 x 32 KBytes, 8-way set associative, 64-byte line size
        	L1 Instruction cache	4 x 32 KBytes, 8-way set associative, 64-byte line size
        	L2 cache	4 x 256 KBytes, 8-way set associative, 64-byte line size
        	L3 cache	6 MBytes, 12-way set associative, 64-byte line size
    

    References