unsigned 64 bit % const

Frank Klemm pfk@fuchs.offl.uni-jena.de
Sun Sep 9 11:29:00 GMT 2001


unsigned long long % const can be reduced for a lot of 'const'.
Note that the following code is not corect, but shows the principle.

----------------------------------------------------------------------------

#include <stdio.h>

typedef unsigned long       u32;
typedef unsigned long long  u64;


void 
error ( u64 x, u32 t )
{
    fprintf ( stderr, "BUG in %llu / %lu\n\a\n", x, t);
}

int
main ( void )
{
    u64  x;
    
    for ( x = 0xFFFFFF00; ; x++ ) {
        if ( (x %   2) != ((u32)x & 1) )                    error (x,   2);
        if ( (x %   3) != ((u32)x +   (u32)(x>>32)) %   3 ) error (x,   3);
        if ( (x %   4) != ((u32)x & 3) )                    error (x,   4);
        if ( (x %   5) != ((u32)x +   (u32)(x>>32)) %   5 ) error (x,   5);
        if ( (x %   6) != ((u32)x + 4*(u32)(x>>32)) %   6 ) error (x,   6);
        if ( (x %   7) != ((u32)x + 4*(u32)(x>>32)) %   7 ) error (x,   7);
        if ( (x %   8) != ((u32)x & 7) )                    error (x,   8);
        if ( (x %   9) != ((u32)x + 4*(u32)(x>>32)) %   9 ) error (x,   9);
        if ( (x %  11) != ((u32)x + 4*(u32)(x>>32)) %  11 ) error (x,  11);
        if ( (x %  17) != ((u32)x +   (u32)(x>>32)) %  17 ) error (x,  17);
        if ( (x %  19) != ((u32)x + 6*(u32)(x>>32)) %  19 ) error (x,  19);
        if ( (x %  31) != ((u32)x + 4*(u32)(x>>32)) %  31 ) error (x,  31);
        if ( (x %  37) != ((u32)x + 7*(u32)(x>>32)) %  37 ) error (x,  37);
        if ( (x % 151) != ((u32)x + 4*(u32)(x>>32)) % 151 ) error (x, 151);
        if ( (x % 167) != ((u32)x + 7*(u32)(x>>32)) % 167 ) error (x, 167);
        if ( (x % 257) != ((u32)x +   (u32)(x>>32)) % 257 ) error (x, 257);
        if ( (x % 331) != ((u32)x + 4*(u32)(x>>32)) % 331 ) error (x, 331);
    }
    
    return 0;
}

--------------------------------------------------------------------------------
You must reduce x in the following way:

	x' = x_low + x_high * k;	where k = 2^32 % divider

x' can have more than 32 bit !!!
But x' is always small than divider * 2^32.

Code to generate for  (var % cst):

--- cst is a power of 2 -----------------------------------------------------

This is trivial!

	movl	var, %eax
	andl	$(cst - 1), %eax

	... the upper 32 bit are 0
	
	xorl	%edx, %edx

--- cst is 3, 5, 17, 257, 65537 (prime factors of 2^32-1) -------------------

Here we're reducing x' to 33 bit and then we are using div

	movl	var, %eax
	xorl	%edx, %edx
	addl	var + 4, %eax
	adcl	%edx, %edx
	movl	$cst, %ecx
	divl	%ecx
	movl	%edx, %eax
	xorl	%edx, %edx

--- cst is 3, 5, 17, 257, 65537 (prime factors of 2^32-1) -------------------

Here we're reducing x' to 32 bit and then we can use other tricky uint32 % const code

        movl    var, %eax
        addl    var + 4, %eax
        adcl    $0, %eax

        ... now a simple (32 bit % cst) to get the lower 32 bit, the upper 32 bit are 0

        xorl    %edx, %edx
        movl    $cst, %ecx
        divl    %ecx            ; may be you have a better solution for uint32 % const
        movl    %edx, %eax
        xorl    %edx, %edx


--- all other with 2^32 % cst < 65536 ---------------------------------------

Here we'r reducing x' to 32 bit and then we can use other tricky uint32 % const code

	k = 0x100000000 % cst, must be < 65536

	movl	$k, %eax
	mull	var + 4		; edx < k
	addl	var, %eax
	adcl	$0, %edx	; edx <= k
	jz	lbl		; should be removed for larger k (what is a larger k???), depends on imul / mispredicted jump trade-off
	imul	$k, %edx	; edx <= k*k
	addl	%edx, %eax
	adcl	$0, %eax
lbl:
	... now a simple (32 bit % cst) to get the lower 32 bit, the upper 32 bit are 0
	
	xorl	%edx, %edx
	movl	$cst, %ecx
	divl	%ecx		; may be you have a better solution for uint32 % const
	movl	%edx, %eax
	xorl	%edx, %edx

--- all other ---------------------------------------------------------------

Here we'r reducing x' to more than 32 bit and then we can use div

	k = 0x100000000 % cst, must be < 65536

	movl	$k, %eax
	mull	var + 4		; edx < k
	addl	var, %eax
	adcl	$0, %edx	; edx <= k < div
	movl	$cst, %ecx
	divl	%ecx		
	movl	%edx, %eax
	xorl	%edx, %edx

-------------------------------------------------------------------------------

This is for uint64 % uint32_const

The other 7 cases are:
	    uint64 % -uint32_const	(is this allowed ???)
	    int64  %  uint32_const
	    int64  % -uint32_const
            uint64 / uint32_const
	    uint64 / -uint32_const	(is this allowed ???)
	    int64  /  uint32_const
	    int64  / -uint32_const

Interest or too tricky? Does someone find bugs ???
	    
-- 
Frank Klemm



More information about the Gcc mailing list