The need for D-scussion (was Re: D Wishlist)

Richard A. O'Keefe ok at quintus.UUCP
Fri Mar 18 17:27:34 AEST 1988


In article <27143 at linus.UUCP>, bs at linus.UUCP (Robert D. Silverman) writes:
> I am a computational number theorist who writes a LOT of code requiring that:
> A*B/C
> A*B  % C
> be computed where A,B,C are all 30 bit quantities.

Here is something I knocked together for a Sun-3.  It relies on the "inline"
processor which comes with SUN's compilers.  Some other versions of UNIX have
it, but I don't know which.  I decided to post this to comp.lang.c because
this approach seems to be far the cleanest way of getting assembly code into
a C program.  You write an ordinary function call, which can be checked by
lint just like any other function call.  The compiler replaces such function
calls by the assembly code of your choice *before* putting the intermediate
code through the -O phase, so parts of the code you wrote can actually be
fused with parts the compiler wrote.

Of course this sort of thing is machine dependent, but the really nice thing
is that it doesn't make the code that USES inlined code machine dependent.
You can provide ordinary C code for such functions if you want to (great for
debugging).

Anyway, here's the code.  THIS CODE IS PROVIDED FREE TO ILLUSTRATE A
TECHNIQUE.  I MAKE NO CLAIM THAT ANY PART OF IT FUNCTIONS CORRECTLY.

#!/bin/sh
cat >muldivrem.il <<'------ EOF ------'
|   File   : muldivrem.il
|   Author : Richard A. O'Keefe
|   Defines: in-line expansion for mul_div_rem

|   The idea is that we want a function
|	int mul_div_rem(A, B, C, D, Q, R)
|	    /* 32-bit */ int A, B, C, D;
|	    /* 32-bit */ int *Q, *R;
|   which does
|	long long int T = A*B+C;
|	if (D == 0 || T/D overflow) {
|	    *Q = high bits of T, *R = low bits of T;
|	    return 0;
|	} else {
|	    *Q = T/D, *R = T%D;
|	    return non-zero;
|	}
|   The fact that we get the high and low bits of T in the case of an
|   overflow is due to the definition of the M68020 divs.l instruction,
|   which doesn't change the destination in this case.  More generally,
|   we might recompute the result.  mul_div_rem with D=0 is useful for
|   getting A*B+C without doing a division.
|   The point of this file is to have the operation realised by in-line
|   assembly code, rather than by a C function call with all its overhead.
|
|    Assumptions:
|	registers d0, d1, d2, and a0 are free.

	.inline	_mul_div_rem,24
	movl	sp at +,d2
	mulsl	sp at +,d1:d2
	clrl	d0
	addl	sp at +,d2
	addxl	d0,d1
	movl	sp at +,d0
	blts	1f		| if D is zero, don't do the division
	divsl	d0,d1:d2
	bvcs	1f		| if quotient overflow happened
	clrl	d0		| set the result to 0
    1:	movl	sp at +,a0
	movl	d2,a0@
	movl	sp at +,a0
	movl	d1,a0@
	.end
------ EOF ------
ls -l muldivrem.il
cat >mdrtest.c <<'------ EOF ------'
/*  File   : mdrtest.c
    Author : Richard A. O'Keefe
    Purpose: Test mul_div_rem
*/

extern int mul_div_rem();

main()
    {
	int a, b, c, d, q, r;

	while ((printf("a,b,c,d:= "), scanf("%d%d%d%d", &a,&b,&c,&d)) == 4) {
	    a = mul_div_rem(a, b, c, d, &q, &r);
	    printf("a=%d, q=%d, r=%d\n", a, q, r);
	}
    }

------ EOF ------
ls -l mdrtest.c
cat >mdrscript <<'------ EOF ------'
#!/bin/sh
#   File   : mdrscript
#   Author : Richard A. O'Keefe
#   Purpose: compile mdrtest

cc -o mdrtest -O mdrtest.c muldivrem.il

------ EOF ------
ls -l mdrscript



More information about the Comp.lang.c mailing list