xref: /openbmc/linux/arch/x86/math-emu/poly_l2.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2da957e11SThomas Gleixner /*---------------------------------------------------------------------------+
3da957e11SThomas Gleixner  |  poly_l2.c                                                                |
4da957e11SThomas Gleixner  |                                                                           |
5da957e11SThomas Gleixner  | Compute the base 2 log of a FPU_REG, using a polynomial approximation.    |
6da957e11SThomas Gleixner  |                                                                           |
7da957e11SThomas Gleixner  | Copyright (C) 1992,1993,1994,1997                                         |
8da957e11SThomas Gleixner  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9da957e11SThomas Gleixner  |                  E-mail   billm@suburbia.net                              |
10da957e11SThomas Gleixner  |                                                                           |
11da957e11SThomas Gleixner  |                                                                           |
12da957e11SThomas Gleixner  +---------------------------------------------------------------------------*/
13da957e11SThomas Gleixner 
14da957e11SThomas Gleixner #include "exception.h"
15da957e11SThomas Gleixner #include "reg_constant.h"
16da957e11SThomas Gleixner #include "fpu_emu.h"
17da957e11SThomas Gleixner #include "fpu_system.h"
18da957e11SThomas Gleixner #include "control_w.h"
19da957e11SThomas Gleixner #include "poly.h"
20da957e11SThomas Gleixner 
21da957e11SThomas Gleixner static void log2_kernel(FPU_REG const *arg, u_char argsign,
22da957e11SThomas Gleixner 			Xsig * accum_result, long int *expon);
23da957e11SThomas Gleixner 
24da957e11SThomas Gleixner /*--- poly_l2() -------------------------------------------------------------+
25da957e11SThomas Gleixner  |   Base 2 logarithm by a polynomial approximation.                         |
26da957e11SThomas Gleixner  +---------------------------------------------------------------------------*/
poly_l2(FPU_REG * st0_ptr,FPU_REG * st1_ptr,u_char st1_sign)27da957e11SThomas Gleixner void poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign)
28da957e11SThomas Gleixner {
29da957e11SThomas Gleixner 	long int exponent, expon, expon_expon;
30da957e11SThomas Gleixner 	Xsig accumulator, expon_accum, yaccum;
31da957e11SThomas Gleixner 	u_char sign, argsign;
32da957e11SThomas Gleixner 	FPU_REG x;
33da957e11SThomas Gleixner 	int tag;
34da957e11SThomas Gleixner 
35da957e11SThomas Gleixner 	exponent = exponent16(st0_ptr);
36da957e11SThomas Gleixner 
37da957e11SThomas Gleixner 	/* From st0_ptr, make a number > sqrt(2)/2 and < sqrt(2) */
383d0d14f9SIngo Molnar 	if (st0_ptr->sigh > (unsigned)0xb504f334) {
39da957e11SThomas Gleixner 		/* Treat as  sqrt(2)/2 < st0_ptr < 1 */
40da957e11SThomas Gleixner 		significand(&x) = -significand(st0_ptr);
41da957e11SThomas Gleixner 		setexponent16(&x, -1);
42da957e11SThomas Gleixner 		exponent++;
43da957e11SThomas Gleixner 		argsign = SIGN_NEG;
443d0d14f9SIngo Molnar 	} else {
45da957e11SThomas Gleixner 		/* Treat as  1 <= st0_ptr < sqrt(2) */
46da957e11SThomas Gleixner 		x.sigh = st0_ptr->sigh - 0x80000000;
47da957e11SThomas Gleixner 		x.sigl = st0_ptr->sigl;
48da957e11SThomas Gleixner 		setexponent16(&x, 0);
49da957e11SThomas Gleixner 		argsign = SIGN_POS;
50da957e11SThomas Gleixner 	}
51da957e11SThomas Gleixner 	tag = FPU_normalize_nuo(&x);
52da957e11SThomas Gleixner 
533d0d14f9SIngo Molnar 	if (tag == TAG_Zero) {
54da957e11SThomas Gleixner 		expon = 0;
55da957e11SThomas Gleixner 		accumulator.msw = accumulator.midw = accumulator.lsw = 0;
563d0d14f9SIngo Molnar 	} else {
57da957e11SThomas Gleixner 		log2_kernel(&x, argsign, &accumulator, &expon);
58da957e11SThomas Gleixner 	}
59da957e11SThomas Gleixner 
603d0d14f9SIngo Molnar 	if (exponent < 0) {
61da957e11SThomas Gleixner 		sign = SIGN_NEG;
62da957e11SThomas Gleixner 		exponent = -exponent;
633d0d14f9SIngo Molnar 	} else
64da957e11SThomas Gleixner 		sign = SIGN_POS;
653d0d14f9SIngo Molnar 	expon_accum.msw = exponent;
663d0d14f9SIngo Molnar 	expon_accum.midw = expon_accum.lsw = 0;
673d0d14f9SIngo Molnar 	if (exponent) {
68da957e11SThomas Gleixner 		expon_expon = 31 + norm_Xsig(&expon_accum);
69da957e11SThomas Gleixner 		shr_Xsig(&accumulator, expon_expon - expon);
70da957e11SThomas Gleixner 
71da957e11SThomas Gleixner 		if (sign ^ argsign)
72da957e11SThomas Gleixner 			negate_Xsig(&accumulator);
73da957e11SThomas Gleixner 		add_Xsig_Xsig(&accumulator, &expon_accum);
743d0d14f9SIngo Molnar 	} else {
75da957e11SThomas Gleixner 		expon_expon = expon;
76da957e11SThomas Gleixner 		sign = argsign;
77da957e11SThomas Gleixner 	}
78da957e11SThomas Gleixner 
793d0d14f9SIngo Molnar 	yaccum.lsw = 0;
803d0d14f9SIngo Molnar 	XSIG_LL(yaccum) = significand(st1_ptr);
81da957e11SThomas Gleixner 	mul_Xsig_Xsig(&accumulator, &yaccum);
82da957e11SThomas Gleixner 
83da957e11SThomas Gleixner 	expon_expon += round_Xsig(&accumulator);
84da957e11SThomas Gleixner 
853d0d14f9SIngo Molnar 	if (accumulator.msw == 0) {
86da957e11SThomas Gleixner 		FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
87da957e11SThomas Gleixner 		return;
88da957e11SThomas Gleixner 	}
89da957e11SThomas Gleixner 
90da957e11SThomas Gleixner 	significand(st1_ptr) = XSIG_LL(accumulator);
91da957e11SThomas Gleixner 	setexponent16(st1_ptr, expon_expon + exponent16(st1_ptr) + 1);
92da957e11SThomas Gleixner 
93da957e11SThomas Gleixner 	tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign ^ st1_sign);
94da957e11SThomas Gleixner 	FPU_settagi(1, tag);
95da957e11SThomas Gleixner 
96da957e11SThomas Gleixner 	set_precision_flag_up();	/* 80486 appears to always do this */
97da957e11SThomas Gleixner 
98da957e11SThomas Gleixner 	return;
99da957e11SThomas Gleixner 
100da957e11SThomas Gleixner }
101da957e11SThomas Gleixner 
102da957e11SThomas Gleixner /*--- poly_l2p1() -----------------------------------------------------------+
103da957e11SThomas Gleixner  |   Base 2 logarithm by a polynomial approximation.                         |
104da957e11SThomas Gleixner  |   log2(x+1)                                                               |
105da957e11SThomas Gleixner  +---------------------------------------------------------------------------*/
poly_l2p1(u_char sign0,u_char sign1,FPU_REG * st0_ptr,FPU_REG * st1_ptr,FPU_REG * dest)106da957e11SThomas Gleixner int poly_l2p1(u_char sign0, u_char sign1,
107da957e11SThomas Gleixner 	      FPU_REG * st0_ptr, FPU_REG * st1_ptr, FPU_REG * dest)
108da957e11SThomas Gleixner {
109da957e11SThomas Gleixner 	u_char tag;
110da957e11SThomas Gleixner 	long int exponent;
111da957e11SThomas Gleixner 	Xsig accumulator, yaccum;
112da957e11SThomas Gleixner 
1133d0d14f9SIngo Molnar 	if (exponent16(st0_ptr) < 0) {
114da957e11SThomas Gleixner 		log2_kernel(st0_ptr, sign0, &accumulator, &exponent);
115da957e11SThomas Gleixner 
116da957e11SThomas Gleixner 		yaccum.lsw = 0;
117da957e11SThomas Gleixner 		XSIG_LL(yaccum) = significand(st1_ptr);
118da957e11SThomas Gleixner 		mul_Xsig_Xsig(&accumulator, &yaccum);
119da957e11SThomas Gleixner 
120da957e11SThomas Gleixner 		exponent += round_Xsig(&accumulator);
121da957e11SThomas Gleixner 
122da957e11SThomas Gleixner 		exponent += exponent16(st1_ptr) + 1;
1233d0d14f9SIngo Molnar 		if (exponent < EXP_WAY_UNDER)
1243d0d14f9SIngo Molnar 			exponent = EXP_WAY_UNDER;
125da957e11SThomas Gleixner 
126da957e11SThomas Gleixner 		significand(dest) = XSIG_LL(accumulator);
127da957e11SThomas Gleixner 		setexponent16(dest, exponent);
128da957e11SThomas Gleixner 
129da957e11SThomas Gleixner 		tag = FPU_round(dest, 1, 0, FULL_PRECISION, sign0 ^ sign1);
130da957e11SThomas Gleixner 		FPU_settagi(1, tag);
131da957e11SThomas Gleixner 
132da957e11SThomas Gleixner 		if (tag == TAG_Valid)
133da957e11SThomas Gleixner 			set_precision_flag_up();	/* 80486 appears to always do this */
1343d0d14f9SIngo Molnar 	} else {
135da957e11SThomas Gleixner 		/* The magnitude of st0_ptr is far too large. */
136da957e11SThomas Gleixner 
1373d0d14f9SIngo Molnar 		if (sign0 != SIGN_POS) {
138da957e11SThomas Gleixner 			/* Trying to get the log of a negative number. */
139da957e11SThomas Gleixner #ifdef PECULIAR_486		/* Stupid 80486 doesn't worry about log(negative). */
140da957e11SThomas Gleixner 			changesign(st1_ptr);
141da957e11SThomas Gleixner #else
142da957e11SThomas Gleixner 			if (arith_invalid(1) < 0)
143da957e11SThomas Gleixner 				return 1;
144da957e11SThomas Gleixner #endif /* PECULIAR_486 */
145da957e11SThomas Gleixner 		}
146da957e11SThomas Gleixner 
147da957e11SThomas Gleixner 		/* 80486 appears to do this */
148da957e11SThomas Gleixner 		if (sign0 == SIGN_NEG)
149da957e11SThomas Gleixner 			set_precision_flag_down();
150da957e11SThomas Gleixner 		else
151da957e11SThomas Gleixner 			set_precision_flag_up();
152da957e11SThomas Gleixner 	}
153da957e11SThomas Gleixner 
154da957e11SThomas Gleixner 	if (exponent(dest) <= EXP_UNDER)
155da957e11SThomas Gleixner 		EXCEPTION(EX_Underflow);
156da957e11SThomas Gleixner 
157da957e11SThomas Gleixner 	return 0;
158da957e11SThomas Gleixner 
159da957e11SThomas Gleixner }
160da957e11SThomas Gleixner 
161da957e11SThomas Gleixner #undef HIPOWER
162da957e11SThomas Gleixner #define	HIPOWER	10
1633d0d14f9SIngo Molnar static const unsigned long long logterms[HIPOWER] = {
164da957e11SThomas Gleixner 	0x2a8eca5705fc2ef0LL,
165da957e11SThomas Gleixner 	0xf6384ee1d01febceLL,
166da957e11SThomas Gleixner 	0x093bb62877cdf642LL,
167da957e11SThomas Gleixner 	0x006985d8a9ec439bLL,
168da957e11SThomas Gleixner 	0x0005212c4f55a9c8LL,
169da957e11SThomas Gleixner 	0x00004326a16927f0LL,
170da957e11SThomas Gleixner 	0x0000038d1d80a0e7LL,
171da957e11SThomas Gleixner 	0x0000003141cc80c6LL,
172da957e11SThomas Gleixner 	0x00000002b1668c9fLL,
173da957e11SThomas Gleixner 	0x000000002c7a46aaLL
174da957e11SThomas Gleixner };
175da957e11SThomas Gleixner 
176da957e11SThomas Gleixner static const unsigned long leadterm = 0xb8000000;
177da957e11SThomas Gleixner 
178da957e11SThomas Gleixner /*--- log2_kernel() ---------------------------------------------------------+
179da957e11SThomas Gleixner  |   Base 2 logarithm by a polynomial approximation.                         |
180da957e11SThomas Gleixner  |   log2(x+1)                                                               |
181da957e11SThomas Gleixner  +---------------------------------------------------------------------------*/
log2_kernel(FPU_REG const * arg,u_char argsign,Xsig * accum_result,long int * expon)182da957e11SThomas Gleixner static void log2_kernel(FPU_REG const *arg, u_char argsign, Xsig *accum_result,
183da957e11SThomas Gleixner 			long int *expon)
184da957e11SThomas Gleixner {
185da957e11SThomas Gleixner 	long int exponent, adj;
186da957e11SThomas Gleixner 	unsigned long long Xsq;
187da957e11SThomas Gleixner 	Xsig accumulator, Numer, Denom, argSignif, arg_signif;
188da957e11SThomas Gleixner 
189da957e11SThomas Gleixner 	exponent = exponent16(arg);
190da957e11SThomas Gleixner 	Numer.lsw = Denom.lsw = 0;
191da957e11SThomas Gleixner 	XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg);
1923d0d14f9SIngo Molnar 	if (argsign == SIGN_POS) {
193da957e11SThomas Gleixner 		shr_Xsig(&Denom, 2 - (1 + exponent));
194da957e11SThomas Gleixner 		Denom.msw |= 0x80000000;
195da957e11SThomas Gleixner 		div_Xsig(&Numer, &Denom, &argSignif);
1963d0d14f9SIngo Molnar 	} else {
197da957e11SThomas Gleixner 		shr_Xsig(&Denom, 1 - (1 + exponent));
198da957e11SThomas Gleixner 		negate_Xsig(&Denom);
1993d0d14f9SIngo Molnar 		if (Denom.msw & 0x80000000) {
200da957e11SThomas Gleixner 			div_Xsig(&Numer, &Denom, &argSignif);
201da957e11SThomas Gleixner 			exponent++;
2023d0d14f9SIngo Molnar 		} else {
203da957e11SThomas Gleixner 			/* Denom must be 1.0 */
2043d0d14f9SIngo Molnar 			argSignif.lsw = Numer.lsw;
2053d0d14f9SIngo Molnar 			argSignif.midw = Numer.midw;
206da957e11SThomas Gleixner 			argSignif.msw = Numer.msw;
207da957e11SThomas Gleixner 		}
208da957e11SThomas Gleixner 	}
209da957e11SThomas Gleixner 
210da957e11SThomas Gleixner #ifndef PECULIAR_486
211da957e11SThomas Gleixner 	/* Should check here that  |local_arg|  is within the valid range */
2123d0d14f9SIngo Molnar 	if (exponent >= -2) {
2133d0d14f9SIngo Molnar 		if ((exponent > -2) || (argSignif.msw > (unsigned)0xafb0ccc0)) {
214da957e11SThomas Gleixner 			/* The argument is too large */
215da957e11SThomas Gleixner 		}
216da957e11SThomas Gleixner 	}
217da957e11SThomas Gleixner #endif /* PECULIAR_486 */
218da957e11SThomas Gleixner 
2193d0d14f9SIngo Molnar 	arg_signif.lsw = argSignif.lsw;
2203d0d14f9SIngo Molnar 	XSIG_LL(arg_signif) = XSIG_LL(argSignif);
221da957e11SThomas Gleixner 	adj = norm_Xsig(&argSignif);
2223d0d14f9SIngo Molnar 	accumulator.lsw = argSignif.lsw;
2233d0d14f9SIngo Molnar 	XSIG_LL(accumulator) = XSIG_LL(argSignif);
224da957e11SThomas Gleixner 	mul_Xsig_Xsig(&accumulator, &accumulator);
225da957e11SThomas Gleixner 	shr_Xsig(&accumulator, 2 * (-1 - (1 + exponent + adj)));
226da957e11SThomas Gleixner 	Xsq = XSIG_LL(accumulator);
227da957e11SThomas Gleixner 	if (accumulator.lsw & 0x80000000)
228da957e11SThomas Gleixner 		Xsq++;
229da957e11SThomas Gleixner 
230da957e11SThomas Gleixner 	accumulator.msw = accumulator.midw = accumulator.lsw = 0;
231da957e11SThomas Gleixner 	/* Do the basic fixed point polynomial evaluation */
232da957e11SThomas Gleixner 	polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER - 1);
233da957e11SThomas Gleixner 
234da957e11SThomas Gleixner 	mul_Xsig_Xsig(&accumulator, &argSignif);
235da957e11SThomas Gleixner 	shr_Xsig(&accumulator, 6 - adj);
236da957e11SThomas Gleixner 
237da957e11SThomas Gleixner 	mul32_Xsig(&arg_signif, leadterm);
238da957e11SThomas Gleixner 	add_two_Xsig(&accumulator, &arg_signif, &exponent);
239da957e11SThomas Gleixner 
240da957e11SThomas Gleixner 	*expon = exponent + 1;
241da957e11SThomas Gleixner 	accum_result->lsw = accumulator.lsw;
242da957e11SThomas Gleixner 	accum_result->midw = accumulator.midw;
243da957e11SThomas Gleixner 	accum_result->msw = accumulator.msw;
244da957e11SThomas Gleixner 
245da957e11SThomas Gleixner }
246