diff --git a/ChangeLog.md b/ChangeLog.md index 0691ccce..426a83bf 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,39 @@ +1.5.1 +===== + +### Significant changes relative to 1.5.0: + +1. Previously, the undocumented `JSIMD_FORCE*` environment variables could be +used to force-enable a particular SIMD instruction set if multiple instruction +sets were available on a particular platform. On x86 platforms, where CPU +feature detection is bulletproof and multiple SIMD instruction sets are +available, it makes sense for those environment variables to allow forcing the +use of an instruction set only if that instruction set is available. However, +since the ARM implementations of libjpeg-turbo can only use one SIMD +instruction set, and since their feature detection code is less bulletproof +(parsing /proc/cpuinfo), it makes sense for the `JSIMD_FORCENEON` environment +variable to bypass the feature detection code and really force the use of NEON +instructions. A new environment variable (`JSIMD_FORCEDSPR2`) was introduced +in the MIPS implementation for the same reasons, and the existing +`JSIMD_FORCENONE` environment variable was extended to that implementation. +These environment variables provide a workaround for those attempting to test +ARM and MIPS builds of libjpeg-turbo in QEMU, which passes through +/proc/cpuinfo from the host system. + +2. libjpeg-turbo previously assumed that AltiVec instructions were always +available on PowerPC platforms, which led to "illegal instruction" errors when +running on PowerPC chips that lack AltiVec support (such as the older 7xx/G3 +and newer e5500 series.) libjpeg-turbo now examines /proc/cpuinfo on +Linux/Android systems and enables AltiVec instructions only if the CPU supports +them. It also now provides two environment variables, `JSIMD_FORCEALTIVEC` and +`JSIMD_FORCENONE`, to force-enable and force-disable AltiVec instructions in +environments where /proc/cpuinfo is an unreliable means of CPU feature +detection (such as when running in QEMU.) On OS X, libjpeg-turbo continues to +assume that AltiVec support is always available, which means that libjpeg-turbo +cannot be used with G3 Macs unless you set the environment variable +`JSIMD_FORCENONE` to `1`. + + 1.5.0 ===== diff --git a/simd/Makefile.am b/simd/Makefile.am index 18e1b2aa..f96ccea5 100644 --- a/simd/Makefile.am +++ b/simd/Makefile.am @@ -88,19 +88,24 @@ endif if SIMD_POWERPC -libsimd_la_SOURCES = jsimd_powerpc.c jsimd_altivec.h jcsample.h \ +noinst_LTLIBRARIES += libsimd_altivec.la + +libsimd_altivec_la_SOURCES = \ jccolor-altivec.c jcgray-altivec.c jcsample-altivec.c \ jdcolor-altivec.c jdmerge-altivec.c jdsample-altivec.c \ jfdctfst-altivec.c jfdctint-altivec.c \ jidctfst-altivec.c jidctint-altivec.c \ jquanti-altivec.c -libsimd_la_CFLAGS = -maltivec +libsimd_altivec_la_CFLAGS = -maltivec jccolor-altivec.lo: jccolext-altivec.c jcgray-altivec.lo: jcgryext-altivec.c jdcolor-altivec.lo: jdcolext-altivec.c jdmerge-altivec.lo: jdmrgext-altivec.c +libsimd_la_SOURCES = jsimd_powerpc.c jsimd_altivec.h jcsample.h +libsimd_la_LIBADD = libsimd_altivec.la + endif AM_CPPFLAGS = -I$(top_srcdir) diff --git a/simd/jsimd_arm.c b/simd/jsimd_arm.c index 754806dc..61cd073e 100644 --- a/simd/jsimd_arm.c +++ b/simd/jsimd_arm.c @@ -125,7 +125,7 @@ init_simd (void) /* Force different settings through environment variables */ env = getenv("JSIMD_FORCENEON"); if ((env != NULL) && (strcmp(env, "1") == 0)) - simd_support &= JSIMD_ARM_NEON; + simd_support = JSIMD_ARM_NEON; env = getenv("JSIMD_FORCENONE"); if ((env != NULL) && (strcmp(env, "1") == 0)) simd_support = 0; diff --git a/simd/jsimd_arm64.c b/simd/jsimd_arm64.c index 7def8f92..09449bb6 100644 --- a/simd/jsimd_arm64.c +++ b/simd/jsimd_arm64.c @@ -142,7 +142,7 @@ init_simd (void) /* Force different settings through environment variables */ env = getenv("JSIMD_FORCENEON"); if ((env != NULL) && (strcmp(env, "1") == 0)) - simd_support &= JSIMD_ARM_NEON; + simd_support = JSIMD_ARM_NEON; env = getenv("JSIMD_FORCENONE"); if ((env != NULL) && (strcmp(env, "1") == 0)) simd_support = 0; diff --git a/simd/jsimd_mips.c b/simd/jsimd_mips.c index 358bbb83..63b8115d 100644 --- a/simd/jsimd_mips.c +++ b/simd/jsimd_mips.c @@ -2,7 +2,7 @@ * jsimd_mips.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014, D. R. Commander. + * Copyright (C) 2009-2011, 2014, 2016, D. R. Commander. * Copyright (C) 2013-2014, MIPS Technologies, Inc., California. * Copyright (C) 2015, Matthieu Darbois. * @@ -77,6 +77,14 @@ init_simd (void) if (!parse_proc_cpuinfo("MIPS 74K")) return; #endif + + /* Force different settings through environment variables */ + env = getenv("JSIMD_FORCEDSPR2"); + if ((env != NULL) && (strcmp(env, "1") == 0)) + simd_support = JSIMD_MIPS_DSPR2; + env = getenv("JSIMD_FORCENONE"); + if ((env != NULL) && (strcmp(env, "1") == 0)) + simd_support = 0; } static const int mips_idct_ifast_coefs[4] = { diff --git a/simd/jsimd_powerpc.c b/simd/jsimd_powerpc.c index 9edd50a1..42dc1e08 100644 --- a/simd/jsimd_powerpc.c +++ b/simd/jsimd_powerpc.c @@ -2,7 +2,7 @@ * jsimd_powerpc.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009-2011, 2014-2015, D. R. Commander. + * Copyright (C) 2009-2011, 2014-2016, D. R. Commander. * Copyright (C) 2015, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, @@ -22,19 +22,106 @@ #include "../jsimddct.h" #include "jsimd.h" +#include +#include +#include + static unsigned int simd_support = ~0; +#if defined(__linux__) || defined(ANDROID) || defined(__ANDROID__) + +#define SOMEWHAT_SANE_PROC_CPUINFO_SIZE_LIMIT (1024 * 1024) + +LOCAL(int) +check_feature (char *buffer, char *feature) +{ + char *p; + if (*feature == 0) + return 0; + if (strncmp(buffer, "cpu", 3) != 0) + return 0; + buffer += 3; + while (isspace(*buffer)) + buffer++; + + /* Check if 'feature' is present in the buffer as a separate word */ + while ((p = strstr(buffer, feature))) { + if (p > buffer && !isspace(*(p - 1))) { + buffer++; + continue; + } + p += strlen(feature); + if (*p != 0 && !isspace(*p)) { + buffer++; + continue; + } + return 1; + } + return 0; +} + +LOCAL(int) +parse_proc_cpuinfo (int bufsize) +{ + char *buffer = (char *)malloc(bufsize); + FILE *fd; + simd_support = 0; + + if (!buffer) + return 0; + + fd = fopen("/proc/cpuinfo", "r"); + if (fd) { + while (fgets(buffer, bufsize, fd)) { + if (!strchr(buffer, '\n') && !feof(fd)) { + /* "impossible" happened - insufficient size of the buffer! */ + fclose(fd); + free(buffer); + return 0; + } + if (check_feature(buffer, "altivec")) + simd_support |= JSIMD_ALTIVEC; + } + fclose(fd); + } + free(buffer); + return 1; +} + +#endif + +/* + * Check what SIMD accelerations are supported. + * + * FIXME: This code is racy under a multi-threaded environment. + */ LOCAL(void) init_simd (void) { char *env = NULL; +#if !defined(__ALTIVEC__) && (defined(__linux__) || defined(ANDROID) || defined(__ANDROID__)) + int bufsize = 1024; /* an initial guess for the line buffer size limit */ +#endif if (simd_support != ~0U) return; - simd_support = JSIMD_ALTIVEC; + simd_support = 0; + +#if defined(__ALTIVEC__) || defined(__APPLE__) + simd_support |= JSIMD_ALTIVEC; +#elif defined(__linux__) || defined(ANDROID) || defined(__ANDROID__) + while (!parse_proc_cpuinfo(bufsize)) { + bufsize *= 2; + if (bufsize > SOMEWHAT_SANE_PROC_CPUINFO_SIZE_LIMIT) + break; + } +#endif /* Force different settings through environment variables */ + env = getenv("JSIMD_FORCEALTIVEC"); + if ((env != NULL) && (strcmp(env, "1") == 0)) + simd_support = JSIMD_ALTIVEC; env = getenv("JSIMD_FORCENONE"); if ((env != NULL) && (strcmp(env, "1") == 0)) simd_support = 0;