]> granicus.if.org Git - libjpeg-turbo/commitdiff
Linux/PPC: Only enable AltiVec if CPU supports it
authorDRC <information@libjpeg-turbo.org>
Wed, 6 Jul 2016 16:58:28 +0000 (16:58 +0000)
committerDRC <information@libjpeg-turbo.org>
Thu, 7 Jul 2016 19:02:44 +0000 (19:02 +0000)
This eliminates "illegal instruction" errors when running libjpeg-turbo
under Linux on PowerPC chips that lack AltiVec support (e.g. the old
7XX/G3 models but also the newer e5500 series.)

ChangeLog.md
simd/Makefile.am
simd/jsimd_powerpc.c

index 965347163dc5154776a4897372be038e087d952b..426a83bfc561a8884b4cc838fac772339bfb002b 100644 (file)
@@ -20,6 +20,19 @@ 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
 =====
index fad6c8c8a5101359de079c674b024be1e57aa565..b8660d1c0ae46ba4c7da714d227bfb8d48a254c3 100644 (file)
@@ -73,19 +73,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)
index 9edd50a19fa146e87de0141e3c4e0a204f00bcd4..42dc1e0868ddbd8e7cb2594d4869696d186aa38f 100644 (file)
@@ -2,7 +2,7 @@
  * jsimd_powerpc.c
  *
  * Copyright 2009 Pierre Ossman <ossman@cendio.se> 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,
 #include "../jsimddct.h"
 #include "jsimd.h"
 
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
 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;