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