dnl Itanium-2 mpn_modexact_1c_odd -- mpn by 1 exact remainder.
dnl Contributed to the GNU project by Kevin Ryde.
dnl Copyright 2003-2005 Free Software Foundation,
Inc.
dnl
This file is part of the GNU MP Library.
dnl
dnl The GNU MP Library is free software
; you can redistribute it and/or modify
dnl it under the terms of either:
dnl
dnl * the GNU Lesser General
Public License as published by the Free
dnl Software Foundation
; either version 3 of the License, or (at your
dnl
option) any later
version.
dnl
dnl
or
dnl
dnl * the GNU General
Public License as published by the Free Software
dnl Foundation
; either version 2 of the License, or (at your option) any
dnl later
version.
dnl
dnl
or both in parallel, as here.
dnl
dnl The GNU MP Library is distributed in the hope that it will be useful, but
dnl WITHOUT ANY WARRANTY
; without even the implied warranty of MERCHANTABILITY
dnl
or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General
Public License
dnl
for more details.
dnl
dnl You should have received copies of the GNU General
Public License
and the
dnl GNU Lesser General
Public License along with the GNU MP Library.
If not,
dnl see
https://www.gnu.org/licenses/.
include(`../config.m4
')
C cycles/limb
C Itanium: 15
C Itanium 2: 8
dnl Usage: ABI32(`
code')
dnl
dnl Emit the given
code only under HAVE_ABI_32.
dnl
define(ABI32,
m4_assert_onearg()
`
ifdef(`HAVE_ABI_32
',`$1')
')
C mp_limb_t mpn_modexact_1c_odd (mp_srcptr src, mp_size_t size,
C mp_limb_t divisor, mp_limb_t
carry)
;
C
C The modexact algorithm is usually conceived as a dependent chain
C
C l = src[i] - c
C q = low(l * inverse)
C c = high(q*divisor) + (src[i]<c)
C
C but we can work the src[i]-c into an xma by calculating si=src[i]*inverse
C separately (off the dependent chain)
and using
C
C q = low(c * inverse + si)
C c = high(q*divisor + c)
C
C
This means the dependent chain is simply xma.l followed by xma.hu,
for a
C total 8 cycles/limb on itanium-2.
C
C The reason xma.hu works
for the new c is that the low of q*divisor is
C src[i]-c (being the whole purpose of the q generated,
and it can be
C verified algebraically).
If there was an underflow from src[i]-c, then
C there will be an
overflow from (src-c)+c, thereby adding 1 to the new c
C the same as the borrow bit (src[i]<c) gives in the first style shown.
C
C Incidentally, fcmp is not an
option for treating src[i]-c, since it
C apparently traps to the kernel
for unnormalized operands like those used
C
and generated by ldf8
and xma. On one GNU/Linux system it took about 1200
C cycles.
C
C
C First Limb:
C
C The first limb uses q = (src[0]-c) * inverse shown in the first style.
C
This lets us get the first q as soon as the inverse is ready, without
C going through si=s*inverse. Basically at the start we have c
and can use
C it
while waiting
for the inverse, whereas
for the second
and subsequent
C limbs it
's the other way around, ie. we have the inverse and are waiting
C
for c.
C
C At .Lentry the first two instructions in the loop have been done already.
C The load of f11=src[1] at the start (predicated on size>=2),
and the
C calculation of q by the initial different scheme.
C
C
C Entry Sequence:
C
C In the entry sequence, the critical path is the calculation of the
C inverse, so
this is begun first
and optimized. Apart from that, ar.lc is
C established nice
and early so the br.cloop
's should predict perfectly.
C
And the load
for the low limbs src[0]
and src[1] can be initiated long
C ahead of where they
're needed.
C
C
C Inverse Calculation:
C
C The initial 8-bit inverse is calculated using a table lookup.
If it hits
C L1 (which is likely
if we
're called several times) then it should take a
C total 4 cycles, otherwise hopefully L2
for 9 cycles.
This is considered
C the best approach, on balance. It could be done bitwise, but that would
C probably be about 14 cycles (2 per bit beyond the first couple).
Or it
C could be taken from 4 bits to 8 with xmpy doubling as used beyond 8 bits,
C but that would be about 11 cycles.
C
C The table is not the same as binvert_limb_table, instead it
's 256 bytes,
C designed to be indexed by the low
byte of the divisor. The divisor is
C always odd, so the relevant
data is every second
byte in the table. The
C padding lets us use zxt1 instead of extr.u, the latter would cost an extra
C cycle because it must go down I0,
and we
're using the first I0 slot to get
C ip. The extra 128 bytes of padding should be insignificant compared to
C typical ia64
code bloat.
C
C Having the table in .text allows us to use IP-relative addressing,
C avoiding a fetch from ltoff. .rodata is apparently not suitable
for use
C IP-relative, it gets a linker relocation
overflow on GNU/Linux.
C
C
C Load Scheduling:
C
C In the main loop, the
data loads are scheduled
for an L2 hit, which means
C 6 cycles
for the
data ready to use. In fact we
end up 7 cycles ahead. In
C any case that scheduling is achieved simply by doing the load (
and xmpy.l
C
for "si") in the immediately preceding iteration.
C
C The main loop requires size >= 2,
and we handle size==1 by an initial
C br.cloop to enter the loop only
if size>1. Since ar.lc is established
C early,
this should predict perfectly.
C
C
C Not done:
C
C Consideration was given to using a plain
"(src[0]-c) % divisor" for
C size==1, but cycle counting suggests about 50
for the sort of approach
C taken by gcc __umodsi3, versus about 47
for the modexact. (Both assuming
C L1 hits
for their respective fetching.)
C
C Consideration was given to a test
for high<divisor
and replacing the last
C loop iteration with instead c-=src[size-1] followed by c+=d
if underflow.
C Branching on high<divisor wouldn
't be good since a mispredict would cost
C more than the loop iteration saved,
and the condition is of course
data
C dependent. So the theory would be to shorten the loop count
if
C high<divisor,
and predicate extra operations at the
end. That would mean
C a gain of 6 when high<divisor,
or a cost of 2
if not.
C
C Whether such a tradeoff is a win on average depends on assumptions about
C how many bits in the high
and the divisor.
If both are uniformly
C distributed then high<divisor about 50% of the
time. But smallish
C divisors (less chance of high<divisor) might be more likely from
C applications (mpz_divisible_ui, mpz_gcd_ui, etc). Though biggish divisors
C would be normal internally from say mpn/generic/perfsqr.c. On balance,
C
for the moment, it
's felt the gain is not really enough to be worth the
C trouble.
C
C
C Enhancement:
C
C Process two source limbs per iteration using a two-limb inverse
and a
C sequence like
C
C ql = low (c * il + sil) quotient low limb
C qlc = high(c * il + sil)
C qh1 = low (c * ih + sih) quotient high, partial
C
C cl = high (ql * d + c)
carry out of low
C qh = low (qlc * 1 + qh1) quotient high limb
C
C new c = high (qh * d + cl)
carry out of high
C
C
This would be 13 cycles/iteration, giving 6.5 cycles/limb. The two limb
C s*inverse as sih:sil = sh:sl * ih:il would be calculated off the dependent
C chain with 4 multiplies. The bigger inverse would take extra
time to
C calculate, but a one limb iteration to handle an odd size could be done as
C soon as 64-bits of inverse were ready.
C
C Perhaps
this could
even extend to a 3 limb inverse, which might promise 17
C
or 18 cycles
for 3 limbs, giving 5.66
or 6.0 cycles/limb.
C
ASM_START()
.explicit
.text
.
align 32
.Ltable:
data1 0,0x01, 0,0xAB, 0,0xCD, 0,0xB7, 0,0x39, 0,0xA3, 0,0xC5, 0,0xEF
data1 0,0xF1, 0,0x1B, 0,0x3D, 0,0xA7, 0,0x29, 0,0x13, 0,0x35, 0,0xDF
data1 0,0xE1, 0,0x8B, 0,0xAD, 0,0x97, 0,0x19, 0,0x83, 0,0xA5, 0,0xCF
data1 0,0xD1, 0,0xFB, 0,0x1D, 0,0x87, 0,0x09, 0,0xF3, 0,0x15, 0,0xBF
data1 0,0xC1, 0,0x6B, 0,0x8D, 0,0x77, 0,0xF9, 0,0x63, 0,0x85, 0,0xAF
data1 0,0xB1, 0,0xDB, 0,0xFD, 0,0x67, 0,0xE9, 0,0xD3, 0,0xF5, 0,0x9F
data1 0,0xA1, 0,0x4B, 0,0x6D, 0,0x57, 0,0xD9, 0,0x43, 0,0x65, 0,0x8F
data1 0,0x91, 0,0xBB, 0,0xDD, 0,0x47, 0,0xC9, 0,0xB3, 0,0xD5, 0,0x7F
data1 0,0x81, 0,0x2B, 0,0x4D, 0,0x37, 0,0xB9, 0,0x23, 0,0x45, 0,0x6F
data1 0,0x71, 0,0x9B, 0,0xBD, 0,0x27, 0,0xA9, 0,0x93, 0,0xB5, 0,0x5F
data1 0,0x61, 0,0x0B, 0,0x2D, 0,0x17, 0,0x99, 0,0x03, 0,0x25, 0,0x4F
data1 0,0x51, 0,0x7B, 0,0x9D, 0,0x07, 0,0x89, 0,0x73, 0,0x95, 0,0x3F
data1 0,0x41, 0,0xEB, 0,0x0D, 0,0xF7, 0,0x79, 0,0xE3, 0,0x05, 0,0x2F
data1 0,0x31, 0,0x5B, 0,0x7D, 0,0xE7, 0,0x69, 0,0x53, 0,0x75, 0,0x1F
data1 0,0x21, 0,0xCB, 0,0xED, 0,0xD7, 0,0x59, 0,0xC3, 0,0xE5, 0,0x0F
data1 0,0x11, 0,0x3B, 0,0x5D, 0,0xC7, 0,0x49, 0,0x33, 0,0x55, 0,0xFF
PROLOGUE(mpn_modexact_1c_odd)
C r32 src
C r33 size
C r34 divisor
C r35
carry
.prologue
.Lhere:
{ .mmi
; add r33 = -1, r33 C M0 size-1
mov r14 = 2 C M1 2
mov r15 = ip C I0 .Lhere
}{.mmi
; setf.sig f6 = r34 C M2 divisor
setf.sig f9 = r35 C M3
carry
zxt1 r3 = r34 C I1 divisor low
byte
}
;;
{ .mmi
; add r3 = .Ltable-.Lhere, r3 C M0 table offset ip and index
sub r16 = 0, r34 C M1 -divisor
.save ar.lc, r2
mov r2 = ar.lc C I0
}{.mmi
; .body
setf.sig f13 = r14 C M2 2 in significand
mov r17 = -1 C M3 -1
ABI32(` zxt4 r33 = r33
') C I1 size extend
}
;;
{ .mmi
; add r3 = r3, r15 C M0 table entry address
ABI32(` addp4 r32 = 0, r32
') C M1 src extend
mov ar.lc = r33 C I0 size-1 loop count
}{.mmi
; setf.sig f12 = r16 C M2 -divisor
setf.sig f8 = r17 C M3 -1
}
;;
{ .mmi
; ld1 r3 = [r3] C M0 inverse, 8 bits
ldf8 f10 = [r32], 8 C M1 src[0]
cmp.
ne p6,p0 = 0, r33 C I0 test size!=1
}
;;
C Wait
for table load.
C Hope
for an L1 hit of 1 cycles to ALU, but could be more.
setf.sig f7 = r3 C M2 inverse, 8 bits
(p6) ldf8 f11 = [r32], 8 C M1 src[1],
if size!=1
;;
C 5 cycles
C f6 divisor
C f7 inverse, being calculated
C f8 -1, will be -inverse
C f9
carry
C f10 src[0]
C f11 src[1]
C f12 -divisor
C f13 2
C f14 scratch
xmpy.l f14 = f13, f7 C 2*i
xmpy.l f7 = f7, f7 C i*i
;;
xma.l f7 = f7, f12, f14 C i*i*-d + 2*i, inverse 16 bits
;;
xmpy.l f14 = f13, f7 C 2*i
xmpy.l f7 = f7, f7 C i*i
;;
xma.l f7 = f7, f12, f14 C i*i*-d + 2*i, inverse 32 bits
;;
xmpy.l f14 = f13, f7 C 2*i
xmpy.l f7 = f7, f7 C i*i
;;
xma.l f7 = f7, f12, f14 C i*i*-d + 2*i, inverse 64 bits
xma.l f10 = f9, f8, f10 C sc = c * -1 + src[0]
;;
ASSERT(p6, `
xmpy.l f15 = f6, f7
;; C divisor*inverse
getf.sig r31 = f15
;;
cmp.
eq p6,p0 = 1, r31 C should == 1
')
xmpy.l f10 = f10, f7 C q = sc * inverse
xmpy.l f8 = f7, f8 C -inverse = inverse * -1
br.cloop.sptk.few.clr .Lentry C main loop,
if size > 1
;;
C size==1, finish up now
xma.hu f9 = f10, f6, f9 C c = high(q * divisor + c)
mov ar.lc = r2 C I0
;;
getf.sig r8 = f9 C M2 return c
br.
ret.sptk.many b0
.Ltop:
C r2 saved ar.lc
C f6 divisor
C f7 inverse
C f8 -inverse
C f9
carry
C f10 src[i] * inverse
C f11 scratch src[i+1]
add r16 = 160, r32
ldf8 f11 = [r32], 8 C src[i+1]
;;
C 2 cycles
lfetch [r16]
xma.l f10 = f9, f8, f10 C q = c * -inverse + si
;;
C 3 cycles
.Lentry:
xma.hu f9 = f10, f6, f9 C c = high(q * divisor + c)
xmpy.l f10 = f11, f7 C si = src[i] * inverse
br.cloop.sptk.few.clr .Ltop
;;
xma.l f10 = f9, f8, f10 C q = c * -inverse + si
mov ar.lc = r2 C I0
;;
xma.hu f9 = f10, f6, f9 C c = high(q * divisor + c)
;;
getf.sig r8 = f9 C M2 return c
br.
ret.sptk.many b0
EPILOGUE()