Generate XCode project

In order to use xcodebuild to build targets for IOS device, we need to provide .xcodeproj, which can be generated through configure at Turicreate root directory. Under the hood, the .xcodeproject is generated by CMake which uses XCode as the underlying generator.

cd <turicreate_root>
./configure --no-visualization --no-python --no-remotefs --target=iphoneos --arch=arm64 --with-capi --builder=xcode

Try to build TuriCreate with debug mode for ios12, with command

cd debug
xcodebuild -j 8 -project Turi.xcodeproj/ -target Recommender -arch arm64 only_active_arch=no -quiet

Not a long time waiting, the error message says:

cd /Users/guihaoliang/Work/guicreate-2/deps/build/libomp/src/ex_libomp && make
[  5%] Built target libomp-needed-headers
[  8%] Building CXX object runtime/src/CMakeFiles/omp.dir/kmp_alloc.cpp.o
[ 11%] Building CXX object runtime/src/CMakeFiles/omp.dir/kmp_atomic.cpp.o
[ 14%] Building CXX object runtime/src/CMakeFiles/omp.dir/kmp_csupport.cpp.o
[ 17%] Building CXX object runtime/src/CMakeFiles/omp.dir/kmp_debug.cpp.o
[ 20%] Building CXX object runtime/src/CMakeFiles/omp.dir/kmp_itt.cpp.o
[ 23%] Building CXX object runtime/src/CMakeFiles/omp.dir/kmp_environment.cpp.o
/Users/guihaoliang/Work/guicreate-2/deps/build/libomp/src/ex_libomp/runtime/src/kmp_environment.cpp:62:10: fatal error: 'crt_externs.h' file not found
#include <crt_externs.h>
         ^~~~~~~~~~~~~~~
1 error generated.
make[3]: *** [runtime/src/CMakeFiles/omp.dir/kmp_environment.cpp.o] Error 1
make[2]: *** [runtime/src/CMakeFiles/omp.dir/all] Error

Having a glance at the related OpenMP source code,

#if KMP_OS_DARWIN
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif

It needs <crt_externs.h> when KMP_OS_DARWIN is defined, which means it compiles against darwin OS (uname -s to get codename for OS).

#if (defined __APPLE__ && defined __MACH__)
#undef KMP_OS_DARWIN
#define KMP_OS_DARWIN 1
#endif

After googling, the fix should be fairly simple. Either copy source file of crt_externs.h or inline expanding #include <crt_externs.h> to actual implementation code, which is about 5 lines.

But wait for a minute, I’m building binary for ios, and why there’s a compiler pre-defined macro __MACH__? Shouldn’t __MACH__ refers to the laptop Machintosh? After a little bit research, __MACH__ stands for the kernel called machintosh, which is the kernel that the successor kernel darwin builds on top of.

Build with ARM architecture specified

The issue is that it runs the code to determine the compiler architecture before setting up the conditional compiler flags, in order to later pass preprocessor flags to source code. What wse need to do is to explicitly add compile flags -arch arm64 to tell compiler (clang) which architecture we want to compile for. arm64 or aarch64 is used here due to the fact that IOS is using ARM architecture.

For more details, feel free to check my another post about compiler architecture flags.

After the arm64 is set, let’s compile the project again,

[ 97%] Building C object runtime/src/CMakeFiles/omp.dir/z_Linux_asm.S.o
/Users/guihaoliang/Work/guicreate-2/deps/build/libomp/src/ex_libomp/runtime/src/z_Linux_asm.S:1546:5: error: unknown directive
error: unknown directive
    .size __kmp_unnamed_critical_addr,8

Tracing the error message, I found asm code causing trouble below.

#if KMP_ARCH_ARM || KMP_ARCH_MIPS
    .data
    .comm .gomp_critical_user_,32,8
    .data
    .align 4
    .global __kmp_unnamed_critical_addr
__kmp_unnamed_critical_addr:
    .4byte .gomp_critical_user_
    .size __kmp_unnamed_critical_addr,4
#endif /* KMP_ARCH_ARM */

#if KMP_ARCH_PPC64 || KMP_ARCH_AARCH64 || KMP_ARCH_MIPS64
    .data
    .comm .gomp_critical_user_,32,8
    .data
    .align 8
    .global __kmp_unnamed_critical_addr
__kmp_unnamed_critical_addr:
    .8byte .gomp_critical_user_
    .size __kmp_unnamed_critical_addr,8 /** line 1546 **/
#endif /* KMP_ARCH_PPC64 || KMP_ARCH_AARCH64 */

Looking at line 1546, clang already knows we intend to cross compile for ARM but the clang we used here is specifically tailored for XCode,

CC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang

which doesn’t support .size instruction.

Summary

At this stage, I can do nothing but give up the attempt to integrate OpenMP into IOS devices.

IMO, it is pretty reasonable for XCode team to make this decision because IOS computing resources are quite limited and much much less powerful than laptops, and enabling OpenMP probably won’t provide much benefit. What’s more, OpenMP makes multi-threading much easier and convenient to use, developers may end up abusing multi-threading in their Apps, causing overall IOS slowdowns.