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