Merge "Add 'cpufeatures' library to perform runtime CPU family/features detection." into eclair

This commit is contained in:
David Turner
2010-02-08 12:35:21 -08:00
committed by Android (Google) Code Review
5 changed files with 355 additions and 0 deletions

View File

@@ -28,6 +28,14 @@ IMPORTANT CHANGES:
More details about ABIs is now available in docs/CPU-ARCH-ABIS.TXT
- Added a new sample static library, named "cpufeatures" to detect
CPU Features at runtime. For now, this can be used to detect the
availability of ARM Advanced SIMD (a.k.a. NEON) instruction support
at runtime in order to provide optimized code paths for specific
operations.
See docs/CPU-FEATURES.TXT for details.
- GCC 4.4.0 is now used by default by the NDK. It generates better code than
GCC 4.2.1, which was used in previous releases. However, the compiler's C++
frontend is also a lot more pedantic regarding certain template constructs

92
ndk/docs/CPU-FEATURES.TXT Normal file
View File

@@ -0,0 +1,92 @@
Android NDK CPU Features detection library:
-------------------------------------------
This NDK provides a small library named "cpufeatures" that can be used at
runtime to detect the target device's CPU family and the optional features
it supports.
Usage:
------
The library is available from sources/cpufeatures. It provides an Android.mk
build script that can be used to build it as a static library.
To use it, you must:
* add 'cpufeatures' to your APP_MODULES list in your Application.mk
* include 'sources/cpufeatures/Android.mk' at the start or end of your
Android.mk file.
* add 'sources/cpufeatures' to your LOCAL_C_INCLUDES definition.
* add 'cpufeatures' to your LOCAL_STATIC_LIBRARIES definition when building
your final shared library.
your source code can then #include <cpu-features.h> to compile against it.
Here is a simple example:
<NDK>/apps/<appname>/Application.mk:
APP_PROJECT_PATH := <project-path>
APP_MODULES := <your-modules> cpufeatures
<project-path>/jni/Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := <your-module-name>
LOCAL_C_INCLUDES += sources/cpufeatures
LOCAL_SRC_FILES := <your-source-files>
LOCAL_STATIC_LIBRARIES += cpufeatures
include $(BUILD_SHARED_LIBRARY)
include sources/cpufeature/Android.mk
Features:
---------
Two functions are provided for now:
AndroidCpuFamily android_getCpuFamily();
Returns the target device's CPU Family as an enum. For now, the only
supported family is ANDROID_CPU_FAMILY_ARM.
uint64_t android_getCpuFeatures();
Returns the set of optional features supported by the device's CPU.
The result is a set of bit-flags, each corresponding to one CPU
Family-specific optional feature.
Currently, only the following flags are defined, for the ARM CPU Family:
ANDROID_CPU_ARM_FEATURE_ARMv7
Indicates that the device's CPU supports the ARMv7-A instruction
set as supported by the "armeabi-v7a" abi (see CPU-ARCH-ABIS.TXT).
This corresponds to Thumb-2 and VFPv3-D16 instructions.
ANDROID_CPU_ARM_FEATURE_VFPv3
Indicates that the device's CPU supports the VFPv3 hardware FPU
instruction set extension. Due to the definition of 'armeabi-v7a',
this will always be the case if ANDROID_CPU_ARM_FEATURE_ARMv7 is
returned.
Note that this corresponds to the minimum profile VFPv3-D16 that
only provides 16 hardware FP registers.
ANDROID_CPU_ARM_FEATURE_NEON
Indicates that the device's CPU supports the ARM Advanced SIMD
(a.k.a. NEON) vector instruction set extension. Note that ARM
mandates that such CPUs also implement VFPv3-D32, which provides
32 hardware FP registers (shared with the NEON unit).
Important Note:
---------------
The cpufeatures library will be updated to support more CPU families and
optional features in the future. It is designed to work as-is on all
official Android platform versions.

View File

@@ -0,0 +1,10 @@
LOCAL_PATH := $(call my-dir)
# If targetting ARM CPUs, build the ARMv7 detection
# function. It will only be called by the main
# library if we detect ARMv7 through a system property.
#
include $(CLEAR_VARS)
LOCAL_MODULE := cpufeatures
LOCAL_SRC_FILES := cpu-features.c
include $(BUILD_STATIC_LIBRARY)

View File

@@ -0,0 +1,219 @@
#include <sys/system_properties.h>
#include <machine/cpu-features.h>
#include <pthread.h>
#include "cpu-features.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
static pthread_once_t g_once;
static AndroidCpuFamily g_cpuFamily;
static uint64_t g_cpuFeatures;
static const int android_cpufeatures_debug = 0;
#define D(...) \
do { \
if (android_cpufeatures_debug) { \
printf(__VA_ARGS__); fflush(stdout); \
} \
} while (0)
/* Read the content of /proc/cpuinfo into a user-provided buffer.
* Return the length of the data, or -1 on error. Does *not*
* zero-terminate the content. Will not read more
* than 'buffsize' bytes.
*/
static int
read_file(const char* pathname, char* buffer, size_t buffsize)
{
int fd, len;
fd = open(pathname, O_RDONLY);
if (fd < 0)
return -1;
do {
len = read(fd, buffer, buffsize);
} while (len < 0 && errno == EINTR);
close(fd);
return len;
}
/* Extract the content of a the first occurence of a given field in
* the content of /proc/cpuinfo and return it as a heap-allocated
* string that must be freed by the caller.
*
* Return NULL if not found
*/
static char*
extract_cpuinfo_field(char* buffer, int buflen, const char* field)
{
int fieldlen = strlen(field);
char* bufend = buffer + buflen;
char* result = NULL;
int len, ignore;
const char *p, *q;
/* Look for first field occurence, and ensures it starts the line.
*/
p = buffer;
bufend = buffer + buflen;
for (;;) {
p = memmem(p, bufend-p, field, fieldlen);
if (p == NULL)
goto EXIT;
if (p == buffer || p[-1] == '\n')
break;
p += fieldlen;
}
/* Skip to the first column followed by a space */
p += fieldlen;
p = memchr(p, ':', bufend-p);
if (p == NULL || p[1] != ' ')
goto EXIT;
/* Find the end of the line */
p += 2;
q = memchr(p, '\n', bufend-p);
if (q == NULL)
q = bufend;
/* Copy the line into a heap-allocated buffer */
len = q-p;
result = malloc(len+1);
if (result == NULL)
goto EXIT;
memcpy(result, p, len);
result[len] = '\0';
EXIT:
return result;
}
/* Checks that a space-separated list of items contains one given 'item'.
* Returns 1 if found, 0 otherwise.
*/
static int
has_list_item(const char* list, const char* item)
{
const char* p = list;
int itemlen = strlen(item);
if (list == NULL)
return 0;
while (*p) {
const char* q;
/* skip spaces */
while (*p == ' ' || *p == '\t')
p++;
/* find end of current list item */
q = p;
while (*q && *q != ' ' && *q != '\t')
q++;
if (itemlen == q-p && !memcmp(p, item, itemlen))
return 1;
/* skip to next item */
p = q;
}
return 0;
}
static void
android_cpuInit(void)
{
g_cpuFamily = ANDROID_CPU_FAMILY_UNKNOWN;
g_cpuFeatures = 0;
#ifdef __ARM_ARCH__
{
char cpuinfo[4096];
int cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
char* features = NULL;
char* architecture = NULL;
g_cpuFamily = ANDROID_CPU_FAMILY_ARM;
D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, cpuinfo_len, cpuinfo);
if (cpuinfo_len < 0) /* should not happen */ {
return;
}
/* Extract architecture from the "CPU Architecture" field,
* Which can be something like 5JTE, 7, or something else.
* We cannot rely on the 'Processor' field here.
*/
{
char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
if (cpuArch != NULL) {
char* end;
long archNumber;
D("found cpuArch = '%s'\n", cpuArch);
/* read the initial decimal number, ignore the rest */
archNumber = strtol(cpuArch, &end, 10);
/* Here we assume that ARMv8 will be upwards compatible with v7
* in the future. Unfortunately, there is no 'Features' field to
* indicate that Thumb-2 is supported.
*/
if (end > cpuArch && archNumber >= 7) {
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
}
free(cpuArch);
}
}
/* Extract the list of CPU features from 'Features' field */
{
char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
if (cpuFeatures != NULL) {
D("found cpuFeatures = '%s'\n", cpuFeatures);
if (has_list_item(cpuFeatures, "vfpv3"))
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
if (has_list_item(cpuFeatures, "neon"))
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON;
free(cpuFeatures);
}
}
}
#endif /* __ARM_ARCH__ */
}
AndroidCpuFamily
android_getCpuFamily(void)
{
pthread_once(&g_once, android_cpuInit);
return g_cpuFamily;
}
uint64_t
android_getCpuFeatures(void)
{
pthread_once(&g_once, android_cpuInit);
return g_cpuFeatures;
}

View File

@@ -0,0 +1,26 @@
#ifndef CPU_FEATURES_H
#define CPU_FEATURES_H
#include <stdint.h>
typedef enum {
ANDROID_CPU_FAMILY_UNKNOWN = 0,
ANDROID_CPU_FAMILY_ARM,
ANDROID_CPU_FAMILY_X86,
ANDROID_CPU_FAMILY_MAX /* do not remove */
} AndroidCpuFamily;
/* Return family of the device's CPU */
extern AndroidCpuFamily android_getCpuFamily(void);
enum {
ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0),
ANDROID_CPU_ARM_FEATURE_VFPv3 = (1 << 1),
ANDROID_CPU_ARM_FEATURE_NEON = (1 << 2),
};
extern uint64_t android_getCpuFeatures(void);
#endif /* CPU_FEATURES_H */