Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2012 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "SkUtilsArm.h"
     10 
     11 #if SK_ARM_NEON_IS_DYNAMIC
     12 
     13 #include <unistd.h>
     14 #include <fcntl.h>
     15 #include <errno.h>
     16 #include <string.h>
     17 #include <pthread.h>
     18 
     19 #if SK_BUILD_FOR_ANDROID
     20 #  include <cpu-features.h>
     21 #endif
     22 
     23 // A function used to determine at runtime if the target CPU supports
     24 // the ARM NEON instruction set. This implementation is Linux-specific.
     25 static bool sk_cpu_arm_check_neon(void) {
     26     // If we fail any of the following, assume we don't have NEON instructions
     27     // This allows us to return immediately in case of error.
     28     bool result = false;
     29 
     30 // Use the Android NDK's cpu-features helper library to detect NEON at runtime.
     31 // See http://crbug.com/164154 to see why this is needed in Chromium for Android.
     32 #ifdef SK_BUILD_FOR_ANDROID
     33 
     34   result = (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
     35 
     36 #else  // SK_BUILD_FOR_ANDROID
     37 
     38     // There is no user-accessible CPUID instruction on ARM that we can use.
     39     // Instead, we must parse /proc/cpuinfo and look for the 'neon' feature.
     40     // For example, here's a typical output (Nexus S running ICS 4.0.3):
     41     /*
     42     Processor       : ARMv7 Processor rev 2 (v7l)
     43     BogoMIPS        : 994.65
     44     Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3
     45     CPU implementer : 0x41
     46     CPU architecture: 7
     47     CPU variant     : 0x2
     48     CPU part        : 0xc08
     49     CPU revision    : 2
     50 
     51     Hardware        : herring
     52     Revision        : 000b
     53     Serial          : 3833c77d6dc000ec
     54     */
     55     char   buffer[4096];
     56 
     57     do {
     58         // open /proc/cpuinfo
     59         int fd = TEMP_FAILURE_RETRY(open("/proc/cpuinfo", O_RDONLY));
     60         if (fd < 0) {
     61             SkDebugf("Could not open /proc/cpuinfo: %s\n", strerror(errno));
     62             break;
     63         }
     64 
     65         // Read the file. To simplify our search, we're going to place two
     66         // sentinel '\n' characters: one at the start of the buffer, and one at
     67         // the end. This means we reserve the first and last buffer bytes.
     68         buffer[0] = '\n';
     69         int size = TEMP_FAILURE_RETRY(read(fd, buffer+1, sizeof(buffer)-2));
     70         close(fd);
     71 
     72         if (size < 0) {  // should not happen
     73             SkDebugf("Could not read /proc/cpuinfo: %s\n", strerror(errno));
     74             break;
     75         }
     76 
     77         SkDebugf("START /proc/cpuinfo:\n%.*s\nEND /proc/cpuinfo\n",
     78                  size, buffer+1);
     79 
     80         // Compute buffer limit, and place final sentinel
     81         char* buffer_end = buffer + 1 + size;
     82         buffer_end[0] = '\n';
     83 
     84         // Now, find a line that starts with "Features", i.e. look for
     85         // '\nFeatures ' in our buffer.
     86         const char features[] = "\nFeatures\t";
     87         const size_t features_len = sizeof(features)-1;
     88 
     89         char*  line = (char*) memmem(buffer, buffer_end - buffer,
     90                                      features, features_len);
     91         if (line == NULL) {  // Weird, no Features line, bad kernel?
     92             SkDebugf("Could not find a line starting with 'Features'"
     93               "in /proc/cpuinfo ?\n");
     94             break;
     95         }
     96 
     97         line += features_len;  // Skip the "\nFeatures\t" prefix
     98 
     99         // Find the end of the current line
    100         char* line_end = (char*) memchr(line, '\n', buffer_end - line);
    101         if (line_end == NULL)
    102             line_end = buffer_end;
    103 
    104         // Now find an instance of 'neon' in the flags list. We want to
    105         // ensure it's only 'neon' and not something fancy like 'noneon'
    106         // so check that it follows a space.
    107         const char neon[] = " neon";
    108         const size_t neon_len = sizeof(neon)-1;
    109         const char* flag = (const char*) memmem(line, line_end - line,
    110                                                 neon, neon_len);
    111         if (flag == NULL)
    112             break;
    113 
    114         // Ensure it is followed by a space or a newline.
    115         if (flag[neon_len] != ' ' && flag[neon_len] != '\n')
    116             break;
    117 
    118         // Fine, we support Arm NEON !
    119         result = true;
    120 
    121     } while (0);
    122 
    123 #endif  // SK_BUILD_FOR_ANDROID
    124 
    125     if (result) {
    126         SkDEBUGF(("Device supports ARM NEON instructions!\n"));
    127     } else {
    128         SkDEBUGF(("Device does NOT support ARM NEON instructions!\n"));
    129     }
    130     return result;
    131 }
    132 
    133 static pthread_once_t  sOnce;
    134 static bool            sHasArmNeon;
    135 
    136 // called through pthread_once()
    137 void sk_cpu_arm_probe_features(void) {
    138     sHasArmNeon = sk_cpu_arm_check_neon();
    139 }
    140 
    141 bool sk_cpu_arm_has_neon(void) {
    142     pthread_once(&sOnce, sk_cpu_arm_probe_features);
    143     return sHasArmNeon;
    144 }
    145 
    146 #endif // SK_ARM_NEON_IS_DYNAMIC
    147