930 lines · 22.4 KB · C
RawDownload
/* * Floating point emulation support for subnormalised numbers on SH4 * architecture This file is derived from the SoftFloat IEC/IEEE * Floating-point Arithmetic Package, Release 2 the original license of * which is reproduced below. * * ======================================================================== * * This C source file is part of the SoftFloat IEC/IEEE Floating-point * Arithmetic Package, Release 2. * * Written by John R. Hauser.  This work was made possible in part by the * International Computer Science Institute, located at Suite 600, 1947 Center * Street, Berkeley, California 94704.  Funding was partially provided by the * National Science Foundation under grant MIP-9311980.  The original version * of this code was written as part of a project to build a fixed-point vector * processor in collaboration with the University of California at Berkeley, * overseen by Profs. Nelson Morgan and John Wawrzynek.  More information * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ * arithmetic/softfloat.html'. * * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT * TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. * * Derivative works are acceptable, even for commercial purposes, so long as * (1) they include prominent notice that the work is derivative, and (2) they * include prominent notice akin to these three paragraphs for those parts of * this code that are retained. * * ======================================================================== * * SH4 modifications by Ismail Dhaoui <ismail.dhaoui@st.com> * and Kamel Khelifi <kamel.khelifi@st.com> */#include <linux/kernel.h>#include <cpu/fpu.h>#include <asm/div64.h> #define LIT64( a ) a##LL typedef char flag;typedef unsigned char uint8;typedef signed char int8;typedef int uint16;typedef int int16;typedef unsigned int uint32;typedef signed int int32; typedef unsigned long long int bits64;typedef signed long long int sbits64; typedef unsigned char bits8;typedef signed char sbits8;typedef unsigned short int bits16;typedef signed short int sbits16;typedef unsigned int bits32;typedef signed int sbits32; typedef unsigned long long int uint64;typedef signed long long int int64; typedef unsigned long int float32;typedef unsigned long long float64; extern void float_raise(unsigned int flags);	/* in fpu.c */extern int float_rounding_mode(void);	/* in fpu.c */ bits64 extractFloat64Frac(float64 a);flag extractFloat64Sign(float64 a);int16 extractFloat64Exp(float64 a);int16 extractFloat32Exp(float32 a);flag extractFloat32Sign(float32 a);bits32 extractFloat32Frac(float32 a);float64 packFloat64(flag zSign, int16 zExp, bits64 zSig);void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr);float32 packFloat32(flag zSign, int16 zExp, bits32 zSig);void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr);float64 float64_sub(float64 a, float64 b);float32 float32_sub(float32 a, float32 b);float32 float32_add(float32 a, float32 b);float64 float64_add(float64 a, float64 b);float64 float64_div(float64 a, float64 b);float32 float32_div(float32 a, float32 b);float32 float32_mul(float32 a, float32 b);float64 float64_mul(float64 a, float64 b);float32 float64_to_float32(float64 a);void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,		   bits64 * z1Ptr);void sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,		   bits64 * z1Ptr);void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr); static int8 countLeadingZeros32(bits32 a);static int8 countLeadingZeros64(bits64 a);static float64 normalizeRoundAndPackFloat64(flag zSign, int16 zExp,					    bits64 zSig);static float64 subFloat64Sigs(float64 a, float64 b, flag zSign);static float64 addFloat64Sigs(float64 a, float64 b, flag zSign);static float32 roundAndPackFloat32(flag zSign, int16 zExp, bits32 zSig);static float32 normalizeRoundAndPackFloat32(flag zSign, int16 zExp,					    bits32 zSig);static float64 roundAndPackFloat64(flag zSign, int16 zExp, bits64 zSig);static float32 subFloat32Sigs(float32 a, float32 b, flag zSign);static float32 addFloat32Sigs(float32 a, float32 b, flag zSign);static void normalizeFloat64Subnormal(bits64 aSig, int16 * zExpPtr,				      bits64 * zSigPtr);static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b);static void normalizeFloat32Subnormal(bits32 aSig, int16 * zExpPtr,				      bits32 * zSigPtr); bits64 extractFloat64Frac(float64 a){	return a & LIT64(0x000FFFFFFFFFFFFF);} flag extractFloat64Sign(float64 a){	return a >> 63;} int16 extractFloat64Exp(float64 a){	return (a >> 52) & 0x7FF;} int16 extractFloat32Exp(float32 a){	return (a >> 23) & 0xFF;} flag extractFloat32Sign(float32 a){	return a >> 31;} bits32 extractFloat32Frac(float32 a){	return a & 0x007FFFFF;} float64 packFloat64(flag zSign, int16 zExp, bits64 zSig){	return (((bits64) zSign) << 63) + (((bits64) zExp) << 52) + zSig;} void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr){	bits64 z; 	if (count == 0) {		z = a;	} else if (count < 64) {		z = (a >> count) | ((a << ((-count) & 63)) != 0);	} else {		z = (a != 0);	}	*zPtr = z;} static int8 countLeadingZeros32(bits32 a){	static const int8 countLeadingZerosHigh[] = {		8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,		3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,		2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,		2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0	};	int8 shiftCount; 	shiftCount = 0;	if (a < 0x10000) {		shiftCount += 16;		a <<= 16;	}	if (a < 0x1000000) {		shiftCount += 8;		a <<= 8;	}	shiftCount += countLeadingZerosHigh[a >> 24];	return shiftCount; } static int8 countLeadingZeros64(bits64 a){	int8 shiftCount; 	shiftCount = 0;	if (a < ((bits64) 1) << 32) {		shiftCount += 32;	} else {		a >>= 32;	}	shiftCount += countLeadingZeros32(a);	return shiftCount; } static float64 normalizeRoundAndPackFloat64(flag zSign, int16 zExp, bits64 zSig){	int8 shiftCount; 	shiftCount = countLeadingZeros64(zSig) - 1;	return roundAndPackFloat64(zSign, zExp - shiftCount,				   zSig << shiftCount); } static float64 subFloat64Sigs(float64 a, float64 b, flag zSign){	int16 aExp, bExp, zExp;	bits64 aSig, bSig, zSig;	int16 expDiff; 	aSig = extractFloat64Frac(a);	aExp = extractFloat64Exp(a);	bSig = extractFloat64Frac(b);	bExp = extractFloat64Exp(b);	expDiff = aExp - bExp;	aSig <<= 10;	bSig <<= 10;	if (0 < expDiff)		goto aExpBigger;	if (expDiff < 0)		goto bExpBigger;	if (aExp == 0) {		aExp = 1;		bExp = 1;	}	if (bSig < aSig)		goto aBigger;	if (aSig < bSig)		goto bBigger;	return packFloat64(float_rounding_mode() == FPSCR_RM_ZERO, 0, 0);      bExpBigger:	if (bExp == 0x7FF) {		return packFloat64(zSign ^ 1, 0x7FF, 0);	}	if (aExp == 0) {		++expDiff;	} else {		aSig |= LIT64(0x4000000000000000);	}	shift64RightJamming(aSig, -expDiff, &aSig);	bSig |= LIT64(0x4000000000000000);      bBigger:	zSig = bSig - aSig;	zExp = bExp;	zSign ^= 1;	goto normalizeRoundAndPack;      aExpBigger:	if (aExp == 0x7FF) {		return a;	}	if (bExp == 0) {		--expDiff;	} else {		bSig |= LIT64(0x4000000000000000);	}	shift64RightJamming(bSig, expDiff, &bSig);	aSig |= LIT64(0x4000000000000000);      aBigger:	zSig = aSig - bSig;	zExp = aExp;      normalizeRoundAndPack:	--zExp;	return normalizeRoundAndPackFloat64(zSign, zExp, zSig); }static float64 addFloat64Sigs(float64 a, float64 b, flag zSign){	int16 aExp, bExp, zExp;	bits64 aSig, bSig, zSig;	int16 expDiff; 	aSig = extractFloat64Frac(a);	aExp = extractFloat64Exp(a);	bSig = extractFloat64Frac(b);	bExp = extractFloat64Exp(b);	expDiff = aExp - bExp;	aSig <<= 9;	bSig <<= 9;	if (0 < expDiff) {		if (aExp == 0x7FF) {			return a;		}		if (bExp == 0) {			--expDiff;		} else {			bSig |= LIT64(0x2000000000000000);		}		shift64RightJamming(bSig, expDiff, &bSig);		zExp = aExp;	} else if (expDiff < 0) {		if (bExp == 0x7FF) {			return packFloat64(zSign, 0x7FF, 0);		}		if (aExp == 0) {			++expDiff;		} else {			aSig |= LIT64(0x2000000000000000);		}		shift64RightJamming(aSig, -expDiff, &aSig);		zExp = bExp;	} else {		if (aExp == 0x7FF) {			return a;		}		if (aExp == 0)			return packFloat64(zSign, 0, (aSig + bSig) >> 9);		zSig = LIT64(0x4000000000000000) + aSig + bSig;		zExp = aExp;		goto roundAndPack;	}	aSig |= LIT64(0x2000000000000000);	zSig = (aSig + bSig) << 1;	--zExp;	if ((sbits64) zSig < 0) {		zSig = aSig + bSig;		++zExp;	}      roundAndPack:	return roundAndPackFloat64(zSign, zExp, zSig); } float32 packFloat32(flag zSign, int16 zExp, bits32 zSig){	return (((bits32) zSign) << 31) + (((bits32) zExp) << 23) + zSig;} void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr){	bits32 z;	if (count == 0) {		z = a;	} else if (count < 32) {		z = (a >> count) | ((a << ((-count) & 31)) != 0);	} else {		z = (a != 0);	}	*zPtr = z;} static float32 roundAndPackFloat32(flag zSign, int16 zExp, bits32 zSig){	flag roundNearestEven;	int8 roundIncrement, roundBits;	flag isTiny; 	/* SH4 has only 2 rounding modes - round to nearest and round to zero */	roundNearestEven = (float_rounding_mode() == FPSCR_RM_NEAREST);	roundIncrement = 0x40;	if (!roundNearestEven) {		roundIncrement = 0;	}	roundBits = zSig & 0x7F;	if (0xFD <= (bits16) zExp) {		if ((0xFD < zExp)		    || ((zExp == 0xFD)			&& ((sbits32) (zSig + roundIncrement) < 0))		    ) {			float_raise(FPSCR_CAUSE_OVERFLOW | FPSCR_CAUSE_INEXACT);			return packFloat32(zSign, 0xFF,					   0) - (roundIncrement == 0);		}		if (zExp < 0) {			isTiny = (zExp < -1)			    || (zSig + roundIncrement < 0x80000000);			shift32RightJamming(zSig, -zExp, &zSig);			zExp = 0;			roundBits = zSig & 0x7F;			if (isTiny && roundBits)				float_raise(FPSCR_CAUSE_UNDERFLOW);		}	}	if (roundBits)		float_raise(FPSCR_CAUSE_INEXACT);	zSig = (zSig + roundIncrement) >> 7;	zSig &= ~(((roundBits ^ 0x40) == 0) & roundNearestEven);	if (zSig == 0)		zExp = 0;	return packFloat32(zSign, zExp, zSig); } static float32 normalizeRoundAndPackFloat32(flag zSign, int16 zExp, bits32 zSig){	int8 shiftCount; 	shiftCount = countLeadingZeros32(zSig) - 1;	return roundAndPackFloat32(zSign, zExp - shiftCount,				   zSig << shiftCount);} static float64 roundAndPackFloat64(flag zSign, int16 zExp, bits64 zSig){	flag roundNearestEven;	int16 roundIncrement, roundBits;	flag isTiny; 	/* SH4 has only 2 rounding modes - round to nearest and round to zero */	roundNearestEven = (float_rounding_mode() == FPSCR_RM_NEAREST);	roundIncrement = 0x200;	if (!roundNearestEven) {		roundIncrement = 0;	}	roundBits = zSig & 0x3FF;	if (0x7FD <= (bits16) zExp) {		if ((0x7FD < zExp)		    || ((zExp == 0x7FD)			&& ((sbits64) (zSig + roundIncrement) < 0))		    ) {			float_raise(FPSCR_CAUSE_OVERFLOW | FPSCR_CAUSE_INEXACT);			return packFloat64(zSign, 0x7FF,					   0) - (roundIncrement == 0);		}		if (zExp < 0) {			isTiny = (zExp < -1)			    || (zSig + roundIncrement <				LIT64(0x8000000000000000));			shift64RightJamming(zSig, -zExp, &zSig);			zExp = 0;			roundBits = zSig & 0x3FF;			if (isTiny && roundBits)				float_raise(FPSCR_CAUSE_UNDERFLOW);		}	}	if (roundBits)		float_raise(FPSCR_CAUSE_INEXACT);	zSig = (zSig + roundIncrement) >> 10;	zSig &= ~(((roundBits ^ 0x200) == 0) & roundNearestEven);	if (zSig == 0)		zExp = 0;	return packFloat64(zSign, zExp, zSig); } static float32 subFloat32Sigs(float32 a, float32 b, flag zSign){	int16 aExp, bExp, zExp;	bits32 aSig, bSig, zSig;	int16 expDiff; 	aSig = extractFloat32Frac(a);	aExp = extractFloat32Exp(a);	bSig = extractFloat32Frac(b);	bExp = extractFloat32Exp(b);	expDiff = aExp - bExp;	aSig <<= 7;	bSig <<= 7;	if (0 < expDiff)		goto aExpBigger;	if (expDiff < 0)		goto bExpBigger;	if (aExp == 0) {		aExp = 1;		bExp = 1;	}	if (bSig < aSig)		goto aBigger;	if (aSig < bSig)		goto bBigger;	return packFloat32(float_rounding_mode() == FPSCR_RM_ZERO, 0, 0);      bExpBigger:	if (bExp == 0xFF) {		return packFloat32(zSign ^ 1, 0xFF, 0);	}	if (aExp == 0) {		++expDiff;	} else {		aSig |= 0x40000000;	}	shift32RightJamming(aSig, -expDiff, &aSig);	bSig |= 0x40000000;      bBigger:	zSig = bSig - aSig;	zExp = bExp;	zSign ^= 1;	goto normalizeRoundAndPack;      aExpBigger:	if (aExp == 0xFF) {		return a;	}	if (bExp == 0) {

Lines 1–500 of 930.