13

I want to build my library for armv6, and there is some neon code that I enable at runtime if the device supports it. The neon code uses neon intrinsics, and to be able to compile it, I must enable armeabi-v7a, but that affects regular c-code (it becomes broken on some low-end devices).

So, if the android build system wasn't excessively intrusive, I wouldn't have to ask questions, but it seems that there is no way for me to compile one file for armv6 and the other file for arm7-neon.

Can anybody give any clues if that's doable?

Edit
Before trying to reply and wasting internet-ink, it should be clear that these are the main points:
1) make only ONE lib.
2) make build that runs on armv6 (pre neon devices, e.g. armeabi).
3) allow this build to also contain NEON code (which could be executed based on run-time cpu detection; cpu detection is outside the scope of the question).
4) NEON code comes from a c/cpp file and is written using neon intrinsics.

Omitting any part of these requirements totally loses the point of the question

demongolem
  • 9,474
  • 36
  • 90
  • 105
Pavel P
  • 15,789
  • 11
  • 79
  • 128
  • I agree that the system is a bit broken since it does not really support this. One solution that is slightly hackish but works would be to compile your code both for armeabi-v7a and armeabi and manually choose which lib to run in the java native library loader at runtime. This requires both libraries to be included in the app, and some fiddling to determine from java if the system supports NEON but at least it works and won't crash for armv5 devices. – Leo Jun 13 '12 at 12:04
  • I'm aware other possible options, but the simplest most natural solution doesn't work with android build system. The only acceptable solution in this case seems to be to convert that neon intrinsics to standalone asm file and use it along with regular armv6 build. – Pavel P Jun 13 '12 at 19:45
  • For Gradle and CMake the google hello-neon example is perfect [**AndroidNDKSamples**](https://github.com/googlesamples/android-ndk) – Joaquín Luis Monleón Irisarri Nov 07 '17 at 08:26

6 Answers6

12

I have recently found another way to work around the limitations of NDK. My case was not related to NEON, but for you the same hack could do the job.

The trick is to use the existing "tag" mechanism of NDK to specify special CFLAGS for a bunch of files. This is how you do it:

First, list the neon-specific sources. You cannot use the .neon suffix as described in docs/CPU-ARM-NEON.html because build-binary.mk will find that you are not targeting armeabi-v7a. I use the following technique:

LOCAL_NEON_SRC_FILES := imgproc/neon_utils.c \
                        videoproc/usingneon.cpp
LOCAL_SRC_FILES := main.c \
                   imgproc/img.c \
                   videoproc/video.cpp

LOCAL_SRC_FILES += $(LOCAL_NEON_SRC_FILES)

Now, define the CFLAGS for NEON:

LOCAL_NEON_CFLAGS := -mfloat-abi=softfp -mfpu=neon -march=armv7

Finally, add the following magical line to your Android.mk:

TARGET-process-src-files-tags += $(call add-src-files-target-cflags, $(LOCAL_NEON_SRC_FILES), $(LOCAL_NEON_CFLAGS))

If you have more than one binary to build, you will probably want $(LOCAL_NEON_SRC_FILES) to be reset by

include $(CLEAR_VARS)

For this, add the following to your Android.mk or to Application.mk:

modules-LOCALS += NEON_SRC_FILES

Note: I have not tried this magic for NEON, I needed it for entirely different purposes. You may need some adaptations to achieve the desired compilation options for your files, and for your project. I am using NDK r.8b, and I did not check if this would work on earlier (or later) versions.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Alex, I didn't try this approach (I don't work on that thing anymore and I'm too busy atm), but your solution definitely looks like the way to do it. When I worked on that, I also used some internal macro but for neon compilation it was easier to do manual build instead using gcc directly. If I coma back to this, I'll post confirmation here – Pavel P Sep 02 '12 at 19:22
  • 1
    Hi Alex, I tested your solution and it seems to work perfectly. Thank you! – Pavel P Jun 19 '13 at 19:41
  • Something went wrong with the edit approval; I definitely support your correction that adds `LOCAL_NEON_CFLAGS` to `modules-LOCALS` list of auto-refreshed variables. But honestly, this will most likely not be necessary, because a) these flags will probably be exactly same for all `LOCAL_MODULE`s, and b) you will probably only use `LOCAL_NEON_CFLAGS :=` and never `LOCAL_NEON_CFLAGS +=`. – Alex Cohn Jun 21 '13 at 12:45
  • 1
    This is brilliant. For the few of us that struggle with this stuff, this answer is worth +100 – Cory Trese Dec 14 '13 at 23:59
  • 1
    Very helpful - thanks. I neglected to add my LOCAL_NEON_SRC_FILES to my LOCAL_SRC_FILES initially, which prevented them compiling and confused me for a while. – android.weasel Jun 02 '14 at 15:32
4

If you put the NEON code in a separate module (static library or shared library), you can manually tune CFLAGS for that module in your Android.mk to your wanting.

If you have C files that use #ifdef __ARM_NEON__ around intrinsics, your best choice would be to put these files in shared libraries - one compiled for v6, another for neon.

I usually load such "supplimentary" libraries directly from Java, so that the main native code does not care about these changes at all.


Update: here is a simple example that uses static library:

Android.mk

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := neon_utils
LOCAL_SRC_FILES := neon_add.c
LOCAL_CFLAGS += -mfloat-abi=softfp -mfpu=neon -march=armv7
include $(BUILD_STATIC_LIBRARY)

NDK_PATH:=$(call my-dir)/../..

include $(CLEAR_VARS)
LOCAL_MODULE    := test_conditional_load
LOCAL_C_INCLUDES := $(NDK_PATH)/sources/cpufeatures
LOCAL_SRC_FILES := main.c
LOCAL_STATIC_LIBRARIES  :=  neon_utils cpufeatures

include $(BUILD_EXECUTABLE)

include $(NDK_PATH)/sources/cpufeatures/Android.mk

main.c

#include <stdio.h>
#include <cpu-features.h>

void neon_add(int32_t * ptr);

int main()
{
    int32_t int32_4[] = {2,3,4,5};

    if (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON)
    {
        neon_add(int32_4);
        printf("neon\n");
    }
    else
    {
        printf("not neon\n");
    }
    printf("values = %d, %d, %d, %d\n", int32_4[0], int32_4[1], int32_4[2], int32_4[3]);
    return 0;
}

neon_add.c

#include <arm_neon.h>

void neon_add(int32_t * ptr)
{
    int32x4_t vin = vld1q_s32(ptr);
    int32x4_t vout = vaddq_s32(vin, vin);
    vst1q_s32(ptr, vout);
}
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Barely related to the question and absolutely doesn't answer it, sorry. I want to have single module where I want to conditionally load neon code. If that neon code came from pure asm I could pass neon flags to assembler (or used asm directives to enable neon), but I cannot do so for neon intrinsics because that could enable compiler to generate code that doesn't run on armv6, e.g. it might generate armv6t2 or armv7 instructions for regular c/c++ code. With regular make file I could write separate rule for one neon file, with android build I'm left to swear and hate it, because I can't do it – Pavel P Jun 12 '12 at 22:07
  • I am sorry, I probably misunderstood what you mean by "conditionally load neon code". You probably have a separate function that you want always to be loaded, but only called on NEON. For that, it's enough to use a static library, as I describe in the update to my answer (to make use of formatting) – Alex Cohn Jun 14 '12 at 07:13
  • There is another alternative that could have solved the problem. Probably GCC has some sort of pragmas to enable armv7a+neon for some blocks of code (for example, in assembler you can you .fpu neon), but I didn't try to investigate. I was more curious if I was plainly blind and didn't see a way to do that in android makefiles. – Pavel P Jun 14 '12 at 18:41
  • Unfortunately, GCC does not support target pragma for ARM, at least up to v. 4.6.3. But I really don't understand why you are so upset with the idea of grouping all NEON specific code into a static lib. The result is exactly equivalent to what you requested. – Alex Cohn Jun 17 '12 at 07:42
  • 1
    I DO use static lib and it's all is inside a static lib. I do not want to put neon code into separate static lib (I have reasons for that). In short, if I can't use neon intrinsics with c-code the other acceptable solution is to convert that neon intrinsics into plain asm and that's the way I did it. Anyways, GCC is really bad at handling neon intrinsics and the only reason I had them at first place is because they came from 3rd party. – Pavel P Jun 18 '12 at 14:03
  • Note that the performance of C -> asm/NEON calls may vary. I was surprised to find that using NEON assembly is not necessarily faster if floating point is not involved. Compiling the caller for v7a abi may also effect performance of the call. – Alex Cohn Jun 20 '12 at 14:00
  • In that case you probably don't know where and how to use it. Neon assembly is uncomparably faster (regardless if floats are involved) as long as you know what you are doing. NEON intrinsics are likely to generate very bad code with gcc though. Regular c compiler won't ever come close to professionally written neon code in specific scenarios (where large arrays of data need processing etc). – Pavel P Jun 21 '12 at 14:17
2

Using this link http://www.kandroid.org/ndk/docs/CPU-ARM-NEON.html

You can selectively build certain source files with NEON support using the .neon file suffix. Hopefully you can work out whether or not this applies to the ABI selection...

You may be able to put your NEON code path in one file, and using the make file conditional as shown above select to build the correct file based on which ABI you are targeting

yano
  • 4,095
  • 3
  • 35
  • 68
  • 1
    not going to work. The android build system is one more example of crapware enforced on everybody. To build for neon you have to use v7 abi, which means that compiler will be free to generate armv6t2 or armv7 instructions. In short it means that if you build something in android for neon, then that entire project won't work on previous arch's. And the biggest problem with that: it's most likely going to work fine because compiler doesn't generate often these new instructions. But if it does at some point, you'll get mysterious sigill crashes. Basically, that android build system is %%%%. – Pavel P Mar 27 '12 at 17:43
  • Ah true I forgot your other requirement to target the old abi... Good luck – yano Mar 28 '12 at 17:34
1

This snippet from the documentation will probably help:

   # define a static library containing our NEON code
   ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
      include $(CLEAR_VARS)
      LOCAL_MODULE    := mylib-neon
      LOCAL_SRC_FILES := mylib-neon.c
      LOCAL_ARM_NEON  := true
      include $(BUILD_STATIC_LIBRARY)
   endif # TARGET_ARCH_ABI == armeabi-v7a

That only compiles mylib-neon when using armeabi-v7a even if you use the same Android.mk file for various ABIs. For armeabi v6 it will skip over it completely.

richq
  • 55,548
  • 20
  • 150
  • 144
  • it's not what I asked. I need to build non v7a build, but it should contain some v7a code that is selected at runtime. To be able to compile v7a inside armv6 project I need make entire project to be v7a. Obviously I can add that neon code as a separate lib, but I already have many libs and it's just dumb to split libs because of android build system limitation. – Pavel P Oct 07 '11 at 00:59
  • In that case your question isn't clear enough. The comment on Pixie's deleted answer would indicate that you want to have a single build with v7a, v6 and NEON code all together. NEON is compatible with some v7a devices but is not compatible with *any* v6 ones. So perhaps what you're trying to do makes no sense - namely mixing NEON and v6. No v6 devices support NEON, so there's no point checking at run time - may as well do it at compile time. – richq Oct 07 '11 at 10:37
  • I know everything about v6/7/neon etc. I know exactly what I need to do, it's just android build pile of junk that stays on my way. In short, my lib supports both v6 and has optimized neon code. Neon path will be selected at runtime if cpu supports it. There is no point for me to check at compile time, I don't want to have 20 builds for all kinds of devices. I need one that runs on all devices. I got it all done by using gcc/ar directly, I simply can't get it done using android build directly. There is no issue with what I'm trying to do, the only problem is android build system – Pavel P Oct 08 '11 at 00:33
0

If you're looking for a simpler implementation:

First, ensure all NEON-capable code is conditionally compiled only for ABI armeabi-v7a, and additionally will only be executed if at runtime it's running on an ARMv7 implementation which includes NEON:

/* 
   bar_better_on_neon.c
 */

#ifdef HAVE_ARMV7
#  include <arm_neon.h>
#  ifdef ANDROID
#    include "cpu-features.h"
#  endif
#endif

#ifdef HAVE_ARMV7 
static int check_for_neon(void)
{
#  ifdef ANDROID
    // Not all Android devices with ARMv7 are guaranteed to have NEON, so check.
    uint64_t features = android_getCpuFeatures();
    return (features & ANDROID_CPU_ARM_FEATURE_ARMv7) && (features & ANDROID_CPU_ARM_FEATURE_NEON);
#  elif defined(__APPLE__)
    return 1;
#  else
    return 0;
#  endif
}
#endif

void bar(void)
{
#ifdef HAVE_ARMV7
    if (check_for_neon()) {

        /* here put neon code */

    } else {
#endif

        /* here put non-neon code */

#ifdef HAVE_ARMV7
    }
#endif
}

Here, the check_for_neon() uses the NDK's cpufeatures library. Then, in your Android.mk file:

LOCAL_SRC_FILES := foo.c bar_better_on_neon.c
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
  # Compile the one file in NEON mode.
  LOCAL_SRC_FILES := $(subst bar_better_on_neon.c, bar_better_on_neon.c.neon,$(LOCAL_SRC_FILES))
  LOCAL_CFLAGS += -DHAVE_ARMV7=1
endif
bleater
  • 5,098
  • 50
  • 48
-1

Yeah, in your Application.mk file add:

APP_ABI := armeabi armeabi-v7a
LOCAL_ARM_NEON := true

The NDK will not add NEON support to armeabi libs as they are mutually exclusive, simply adding those lines and compiling as you normally do will produce two seperate so files. You include them both in your apk in the directory structure they were exported to and the device will load the neon one automatically if it is supported.

Justin Buser
  • 2,813
  • 25
  • 32
  • armeabi doesn't mean that it can't have NEON. You probably didn't understand the question. The answer is: it's impossible to do with android build system – Pavel P Jul 11 '12 at 18:46
  • 2
    Yes it does, NEON is not available pre-v7a, perhaps YOU didn't understand what you were asking. The answer for how to "compile one file for armv6 and the other file for arm7-neon" is exactly what I posted. Running ndk-build will compile TWO SEPARATE libraries, one regular armeabi (armv6-) and one armeabi-v7a WITH NEON SUPPORT. Claiming that "it's impossible to do with android build system" is both bad English, and bad information. The "android build system" is open source and extremely flexible if you know what you're doing. – Justin Buser Jul 15 '12 at 13:38
  • You have no clue what you are talking about. You say it will compile TWO SEPARATE libraries. Tell me how to put these two libraries in ONE SINGLE library using extremely flexible open source build system. I don't want to explain what's already written in the original question. Read it to understand. I've been doing android for years, I probably wouldn't come asking dumb question how to create armeabi and armeabi-v7 builds. read the question, don't answer if you don't understand or if you have issues with understanding english. – Pavel P Jul 16 '12 at 03:11
  • I added clarifications to the original question. So you understand. armeabi-v7 is just android's name for abi that supports neon do handle extra registers. You still can have neon code in regular armeabi build. If you think I'm wrong, don't bother replying. Thanks. – Pavel P Jul 16 '12 at 03:24