[cfe-commits] [Patch] compiler-rt: SPARC64-optimized multiply/divide

Ed Schouten ed at 80386.nl
Thu Oct 18 01:00:07 PDT 2012


Hi all,

Attached is the latest version of a patch we use at FreeBSD to add
optimized multiply/divide functions on SPARC64. Description from the
original bug report[1]:

"According to a developer at the FreeBSD project, FreeBSD's total compilation
time increases by 2.6% when the host system is built against compiler-rt
instead of libgcc. This is likely due to the fact that GCC has assembly-written
versions of the division and modulo routines, while compiler-rt does not.

The division and modulo routines used by GCC can easily be re-used by
compiler-rt. They are provided for free in The SPARC Architecture Manual
Version 8. Attached to this bug report is a patch that I have written for
compiler-rt. It contains the M4 file that is listed in the manual, with some
small modifications:

- The M4 file uses exponentiation (2^N). This seems to be a Sun-specific
extension to M4, as I cannot reproduce it with GNU and BSD m4. Fix this similar
to OpenBSD's version by replacing 2^N with TWOSUPN.

- Use the same register layout as GCC's version.

- Integrate into compiler-rt's codebase by using DEFINE_COMPILERRT_FUNCTION()."

The diff includes a `generate.sh', which generates the actual assembly
files. I guess we don't want to depend on M4 to build compiler-rt, so
it may be easier to run generate.sh and store the resulting C files in
the repository as well. Sorry for not providing a CMakefile. At
FreeBSD, we never build compiler-rt with CMake, as we imported
compiler-rt into our own build infrastructure.

-- 
Ed Schouten <ed at 80386.nl>

[1] http://llvm.org/bugs/show_bug.cgi?id=11667
-------------- next part --------------
Index: lib/sparc64/divmod.m4
===================================================================
--- lib/sparc64/divmod.m4	(Revision 0)
+++ lib/sparc64/divmod.m4	(Arbeitskopie)
@@ -0,0 +1,259 @@
+//===-- lib/sparc64/divmod.m4 - Integer division and modulo ----*- asm -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements division and modulo routines in SPARC64
+// assembly for the compiler-rt library.  This m4 code has been taken
+// from The SPARC Architecture Manual Version 8.
+//
+//===----------------------------------------------------------------------===//
+
+/*
+ * Division/Remainder
+ *
+ * Input is:
+ *   dividend -- the thing being divided
+ *   divisor -- how many ways to divide it
+ * Important parameters:
+ *   N -- how many bits per iteration we try to get
+ *        as our current guess: define(N, 4) define(TWOSUPN, 16)
+ *   WORDSIZE -- how many bits altogether we're talking about:
+ *               obviously: define(WORDSIZE, 32)
+ * A derived constant:
+ *   TOPBITS -- how many bits are in the top "decade" of a number:
+ *        define(TOPBITS, eval( WORDSIZE - N*((WORDSIZE-1)/N) ) )
+ * Important variables are:
+ *   Q -- the partial quotient under development -- initially 0
+ *   R -- the remainder so far -- initially == the dividend
+ *   ITER -- number of iterations of the main division loop which will
+ *           be required. Equal to CEIL( lg2(quotient)/N )
+ *           Note that this is log_base_(2ˆN) of the quotient.
+ *   V -- the current comparand -- initially divisor*2ˆ(ITER*N-1)
+ * Cost:
+ *   current estimate for non-large dividend is
+ *        CEIL( lg2(quotient) / N ) x ( 10 + 7N/2 ) + C
+ *   a large dividend is one greater than 2ˆ(31-TOPBITS) and takes a
+ *   different path, as the upper bits of the quotient must be developed
+ *   one bit at a time.
+ *   This uses the m4 and cpp macro preprocessors.
+ */
+
+define(dividend, `%o0')
+define(divisor,`%o1')
+define(Q, `%o2')
+define(R, `%o3')
+define(ITER, `%o4')
+define(V, `%o5')
+define(SIGN, `%g3')
+define(T, `%g1')
+define(SC,`%g2')
+/*
+ * This is the recursive definition of how we develop quotient digits.
+ * It takes three important parameters:
+ *   $1 -- the current depth, 1<=$1<=N
+ *   $2 -- the current accumulation of quotient bits
+ *   N -- max depth
+ * We add a new bit to $2 and either recurse or insert the bits in the quotient.
+ * Dynamic input:
+ *   R -- current remainder
+ *   Q -- current quotient
+ *   V -- current comparand
+ *   cc -- set on current value of R
+ * Dynamic output:
+ *   R', Q', V', cc'
+ */
+
+#include "../assembly.h"
+
+define(DEVELOP_QUOTIENT_BITS,
+`	!depth $1, accumulated bits $2
+	bl	L.$1.eval(TWOSUPN+$2)
+	srl	V,1,V
+	! remainder is nonnegative
+	subcc	R,V,R
+	ifelse( $1, N,
+	`	b	9f
+		add	Q, ($2*2+1), Q
+	',` DEVELOP_QUOTIENT_BITS( incr($1), `eval(2*$2+1)')
+	')
+L.$1.eval(TWOSUPN+$2):
+	! remainder is negative
+	addcc	R,V,R
+	ifelse( $1, N,
+	`	b	9f
+		add	Q, ($2*2-1), Q
+	',` DEVELOP_QUOTIENT_BITS( incr($1), `eval(2*$2-1)')
+	')
+	ifelse( $1, 1, `9:')
+')
+ifelse( ANSWER, `quotient', `
+.text
+	.align 32
+DEFINE_COMPILERRT_FUNCTION(__udivsi3)
+	b	divide
+	mov	0,SIGN			! result always nonnegative
+.text
+	.align 32
+DEFINE_COMPILERRT_FUNCTION(__divsi3)
+	orcc	divisor,dividend,%g0	! are either dividend or divisor negative
+	bge	divide			! if not, skip this junk
+	xor	divisor,dividend,SIGN	! record sign of result in sign of SIGN
+	tst	divisor
+	bge	2f
+	tst	dividend
+	! divisor < 0
+	bge	divide
+	neg	divisor
+	2:
+	! dividend < 0
+	neg	dividend
+	! FALL THROUGH
+',`
+.text
+	.align 32
+DEFINE_COMPILERRT_FUNCTION(__umodsi3)
+	b	divide
+	mov	0,SIGN			! result always nonnegative
+.text
+	.align 32
+DEFINE_COMPILERRT_FUNCTION(__modsi3)
+	orcc	divisor,dividend,%g0	! are either dividend or divisor negative
+	bge	divide			! if not, skip this junk
+	mov	dividend,SIGN		! record sign of result in sign of SIGN
+	tst	divisor
+	bge	2f
+	tst	dividend
+	! divisor < 0
+	bge	divide
+	neg	divisor
+	2:
+	! dividend < 0
+	neg	dividend
+	! FALL THROUGH
+')
+
+divide:
+	! Compute size of quotient, scale comparand.
+	orcc	divisor,%g0,V		! movcc divisor,V
+	te	2			! if divisor = 0
+	mov	dividend,R
+	mov	0,Q
+	sethi	%hi(1<<(WORDSIZE-TOPBITS-1)),T
+	cmp	R,T
+	blu	not_really_big
+	mov	0,ITER
+	!
+	! Here, the dividend is >= 2ˆ(31-N) or so. We must be careful here,
+	! as our usual N-at-a-shot divide step will cause overflow and havoc.
+	! The total number of bits in the result here is N*ITER+SC, where
+	! SC <= N.
+	! Compute ITER in an unorthodox manner: know we need to Shift V into
+! the top decade: so don't even bother to compare to R.
+1:
+	cmp	V,T
+	bgeu	3f
+	mov	1,SC
+	sll	V,N,V
+	b	1b
+	inc	ITER
+! Now compute SC
+2:	addcc	V,V,V
+	bcc	not_too_big
+	add	SC,1,SC
+		! We're here if the divisor overflowed when Shifting.
+		! This means that R has the high-order bit set.
+		! Restore V and subtract from R.
+		sll	T,TOPBITS,T	! high order bit
+		srl	V,1,V		! rest of V
+		add	V,T,V
+		b	do_single_div
+		dec	SC
+not_too_big:
+3:	cmp	V,R
+	blu	2b
+	nop
+	be	do_single_div
+	nop
+! V > R: went too far: back up 1 step
+!     srl V,1,V
+!      dec SC
+! do single-bit divide steps
+!
+! We have to be careful here. We know that R >= V, so we can do the
+! first divide step without thinking. BUT, the others are conditional,
+! and are only done if R >= 0. Because both R and V may have the high-
+! order bit set in the first step, just falling into the regular
+! division loop will mess up the first time around.
+! So we unroll slightly...
+do_single_div:
+	deccc	SC
+	bl	end_regular_divide
+	nop
+	sub	R,V,R
+	mov	1,Q
+	b,a	end_single_divloop
+	! EMPTY
+single_divloop:
+	sll	Q,1,Q
+	bl	1f
+	srl	V,1,V
+	! R >= 0
+		sub	R,V,R
+		b	2f
+		inc	Q
+	1:	! R < 0
+		add	R,V,R
+		dec	Q
+	2:
+	end_single_divloop:
+		deccc	SC
+		bge	single_divloop
+		tst	R
+		b,a	end_regular_divide
+		! EMPTY
+
+not_really_big:
+1:
+	sll	V,N,V
+	cmp	V,R
+	bleu	1b
+	inccc	ITER
+	be	got_result
+	dec	ITER
+do_regular_divide:
+	! Do the main division iteration
+	tst	R
+	! Fall through into divide loop
+divloop:
+	sll	Q,N,Q
+	DEVELOP_QUOTIENT_BITS( 1, 0 )
+end_regular_divide:
+	deccc	ITER
+	bge	divloop
+	tst	R
+	bl,a	got_result
+	! non-restoring fixup if remainder < 0, otherwise annulled
+ifelse( ANSWER, `quotient',
+`	dec	Q
+',`	add	R,divisor,R
+')
+
+got_result:
+	tst	SIGN
+	bl,a	1f
+	! negate for answer < 0, otherwise annulled
+ifelse( ANSWER, `quotient',
+`	neg	%o2,%o2			! Q <- -Q
+',`	neg	%o3,%o3 		! R <- -R
+')
+1:
+	retl				! leaf-routine return
+ifelse( ANSWER, `quotient',
+`	mov	%o2,%o0			! quotient <- Q
+',`	mov	%o3,%o0			! remainder <- R
+')
Index: lib/sparc64/generate.sh
===================================================================
--- lib/sparc64/generate.sh	(Revision 0)
+++ lib/sparc64/generate.sh	(Arbeitskopie)
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+m4 divmod.m4 | sed -e 's/[[:space:]]*$//' | grep -v '^$' > modsi3.S
+m4 -DANSWER=quotient divmod.m4 | sed -e 's/[[:space:]]*$//' | grep -v '^$' > divsi3.S
Index: lib/sparc64/udivsi3.S
===================================================================
--- lib/sparc64/udivsi3.S	(Revision 0)
+++ lib/sparc64/udivsi3.S	(Arbeitskopie)
@@ -0,0 +1,5 @@
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+
+// This file is intentionally left blank, as __udivsi3 is already
+// provided by divsi3.S.
Index: lib/sparc64/umodsi3.S
===================================================================
--- lib/sparc64/umodsi3.S	(Revision 0)
+++ lib/sparc64/umodsi3.S	(Arbeitskopie)
@@ -0,0 +1,5 @@
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+
+// This file is intentionally left blank, as __umodsi3 is already
+// provided by modsi3.S.


More information about the cfe-commits mailing list