[base] Update FT_MulFix inlining.

Resolves inconsistencies in 64-bit multiplication discussed in !355.                                                    Importantly, FT_MulFix arguments and return value is FT_Long,                                                           whatever sizeof FT_Long is on 64-bit platforms: 8 bytes on Linux or                                                     4 bytes on Windows.                                                                                                                                                                                                                             * include/freetype/internal/ftcalc.h (FT_MulFix_x86_64): Removed.                                                       (FT_MulFix_64): Generalize and prioritize the inline implementation
for all 64-bit platforms ifdef FT_INT64.                                                                                * src/base/ftcalc.c (FT_MulFix)[FT_INT64]: Call 'FT_MulFix_64'.                                                         * src/base/ftbase.c: Include 'ftcalc.c' after the FT_MulFix callers                                                     to enable its inlining.
This commit is contained in:
Alexei Podtelezhnikov 2025-04-09 13:37:23 +00:00
parent fc67794e15
commit 8f67545d72
3 changed files with 30 additions and 75 deletions

View file

@ -33,9 +33,27 @@ FT_BEGIN_HEADER
*
*/
#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER
/* Provide assembler fragments for performance-critical functions. */
/* These must be defined `static __inline__' with GCC. */
#ifdef FT_CONFIG_OPTION_INLINE_MULFIX
#ifdef FT_INT64
static inline FT_Long
FT_MulFix_64( FT_Long a,
FT_Long b )
{
FT_Int64 ab = (FT_Int64)a * b;
ab += 0x8000 + ( ab >> 63 ); /* rounding phase */
return (FT_Long)( ab >> 16 );
}
#define FT_MulFix( a, b ) FT_MulFix_64( a, b )
#elif !defined( FT_CONFIG_OPTION_NO_ASSEMBLER )
/* Provide 32-bit assembler fragments for optimized FT_MulFix. */
/* These must be defined `static __inline__' or similar. */
#if defined( __CC_ARM ) || defined( __ARMCC__ ) /* RVCT */
@ -177,73 +195,11 @@ FT_BEGIN_HEADER
#endif /* _MSC_VER */
#if defined( __GNUC__ ) && defined( __x86_64__ )
#define FT_MULFIX_ASSEMBLER FT_MulFix_x86_64
static __inline__ FT_Int32
FT_MulFix_x86_64( FT_Int32 a,
FT_Int32 b )
{
/* Temporarily disable the warning that C90 doesn't support */
/* `long long'. */
#if __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 )
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlong-long"
#endif
#if 1
/* Technically not an assembly fragment, but GCC does a really good */
/* job at inlining it and generating good machine code for it. */
long long ret, tmp;
ret = (long long)a * b;
tmp = ret >> 63;
ret += 0x8000 + tmp;
return (FT_Int32)( ret >> 16 );
#else
/* For some reason, GCC 4.6 on Ubuntu 12.04 generates invalid machine */
/* code from the lines below. The main issue is that `wide_a' is not */
/* properly initialized by sign-extending `a'. Instead, the generated */
/* machine code assumes that the register that contains `a' on input */
/* can be used directly as a 64-bit value, which is wrong most of the */
/* time. */
long long wide_a = (long long)a;
long long wide_b = (long long)b;
long long result;
__asm__ __volatile__ (
"imul %2, %1\n"
"mov %1, %0\n"
"sar $63, %0\n"
"lea 0x8000(%1, %0), %0\n"
"sar $16, %0\n"
: "=&r"(result), "=&r"(wide_a)
: "r"(wide_b)
: "cc" );
return (FT_Int32)result;
#endif
#if __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 )
#pragma GCC diagnostic pop
#endif
}
#endif /* __GNUC__ && __x86_64__ */
#define FT_MulFix( a, b ) FT_MULFIX_ASSEMBLER( (FT_Int32)(a), (FT_Int32)(b) )
#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */
#ifdef FT_CONFIG_OPTION_INLINE_MULFIX
#ifdef FT_MULFIX_ASSEMBLER
#define FT_MulFix( a, b ) FT_MULFIX_ASSEMBLER( (FT_Int32)(a), (FT_Int32)(b) )
#endif
#endif
#endif /* FT_CONFIG_OPTION_INLINE_MULFIX */
/**************************************************************************

View file

@ -19,7 +19,6 @@
#define FT_MAKE_OPTION_SINGLE_OBJECT
#include "ftadvanc.c"
#include "ftcalc.c"
#include "ftcolor.c"
#include "ftdbgmem.c"
#include "fterrors.c"
@ -35,6 +34,7 @@
#include "ftsnames.c"
#include "ftstream.c"
#include "fttrigon.c"
#include "ftcalc.c" /* included after FT_MulFix callers to inline it */
#include "ftutil.c"

View file

@ -38,13 +38,12 @@
#include <freetype/internal/ftdebug.h>
#include <freetype/internal/ftobjs.h>
#ifdef FT_MULFIX_ASSEMBLER
/* cancel inlining macro from internal/ftcalc.h */
#ifdef FT_MulFix
#undef FT_MulFix
#endif
/* we need to emulate a 64-bit data type if a real one isn't available */
/* we need to emulate a 64-bit data type if one isn't available */
#ifndef FT_INT64
typedef struct FT_Int64_
@ -225,9 +224,9 @@
FT_MulFix( FT_Long a_,
FT_Long b_ )
{
#ifdef FT_MULFIX_ASSEMBLER
#ifdef FT_CONFIG_OPTION_INLINE_MULFIX
return FT_MULFIX_ASSEMBLER( (FT_Int32)a_, (FT_Int32)b_ );
return FT_MulFix_64( a_, b_ );
#else
@ -236,7 +235,7 @@
/* this requires arithmetic right shift of signed numbers */
return (FT_Long)( ( ab + 0x8000L - ( ab < 0 ) ) >> 16 );
#endif /* FT_MULFIX_ASSEMBLER */
#endif /* FT_CONFIG_OPTION_INLINE_MULFIX */
}