required_arithmetic.h   [plain text]



/*
 *	required_arithmetic.h
 *
 *		by Ian Ollmann
 *
 *	Copyright (c) Apple Inc, 2007. All Rights Reserved.
 *
 *	The function of this header is to provide various cLibm routines with
 *	interfaces that cause arithmetic to *always* happen. This header is needed
 *	in various places in Libm, because we use arithmetic to set floating point
 *	state flags. Frequently the product of that arithmetic is not used for the
 *	numerical result of the function, so the compiler erroneously optimizes 
 *	it away. The intent of this header is to provide basic operators that 
 *	utilize compiler and/or platform specific devices (such as volatile asms) 
 *	to prevent the compiler from optimizing away the arithmetic.  If no such
 *	device is available, you can encapsulate these operations as non-inlined
 *	functions with the implementation in a separate compilation unit or static 
 *	library as required to prevent the compiler from removing them.
 *
 *	Sample implementations are provided for __i386__, both as sample code and
 *	so we can test the library.  Other architectures are required to provide
 *	their own implementation.
 *
 */
 
#if defined( __GNUC__ )
	#define ALWAYS_INLINE_NO_DEBUG	__attribute__ (( __always_inline__, __nodebug__ ))
#else
	#define ALWAYS_INLINE_NO_DEBUG
#endif

#ifndef __arm__
	#error  This file is for ARM with VFP only
#endif


#pragma mark -
#pragma mark Declarations

static inline float required_add_float( float a, float b )	ALWAYS_INLINE_NO_DEBUG;
static inline float required_multiply_float( float a, float b ) ALWAYS_INLINE_NO_DEBUG;
static inline float required_divide_float( float a, float b ) ALWAYS_INLINE_NO_DEBUG;
static inline int required_convert_float_to_int( float a ) ALWAYS_INLINE_NO_DEBUG;

static inline double required_add_double( double a, double b ) ALWAYS_INLINE_NO_DEBUG;
static inline double required_multiply_double( double a, double b ) ALWAYS_INLINE_NO_DEBUG;
static inline double required_divide_double( double a, double b ) ALWAYS_INLINE_NO_DEBUG;
static inline int required_convert_double_to_int( double a ) ALWAYS_INLINE_NO_DEBUG;

#pragma mark -
#pragma mark Implementation

// --------------------- single precision -------------------------

static inline float required_add_float( float a, float b )
{
	register float r;
	__asm__ __volatile__ ( "fadds	%0, %1, %2" : "=w" (r) : "w" (a), "w" (b) );
	return r;
}

static inline float required_multiply_float( float a, float b )
{
	register float r;
	__asm__ __volatile__ ( "fmuls	%0, %1, %2" : "=w" (r) : "w" (a), "w" (b) );
	return r;
}

static inline float required_divide_float( float a, float b )
{
	register float r;
	__asm__ __volatile__ ( "fdivs	%0, %1, %2" : "=w" (r) : "w" (a), "w" (b) );
	return r;
}

//rounds toward zero
static inline int required_convert_float_to_int( float a )
{
	register float temp;
	register int result;
	
	__asm__ __volatile__ ( "ftosizs %0, %1" : "=w" (temp) : "w" (a) );
	__asm__ __volatile__ ( "fmrs %0, %1" : "=r" (result) : "w" (temp) );

	return result;
}

// ---------------- double precision --------------------------

static inline double required_add_double( double a, double b )
{
	register double r;
	__asm__ __volatile__ ( "faddd	%P0, %P1, %P2" : "=w" (r) : "w" (a), "w" (b) );
	return r;
}

static inline double required_multiply_double( double a, double b )
{
	register double r;
	__asm__ __volatile__ ( "fmuld	%P0, %P1, %P2" : "=w" (r) : "w" (a), "w" (b) );
	return r;
}

static inline double required_divide_double( double a, double b )
{
	register double r;
	__asm__ __volatile__ ( "fdivd	%P0, %P1, %P2" : "=w" (r) : "w" (a), "w" (b) );
	return r;
}

//rounds toward zero
static inline int required_convert_double_to_int( double a )
{
	register float temp;
	register int result;
	
	__asm__ __volatile__ ( "ftosizd %0, %P1" : "=w" (temp) : "w" (a) );
	__asm__ __volatile__ ( "fmrs %0, %1" : "=r" (result) : "w" (temp) );

	return result;
}