diff --git a/README.arm.md b/README.arm.md index 1c50538172cb9..f31eed9d51ef3 100644 --- a/README.arm.md +++ b/README.arm.md @@ -1,7 +1,12 @@ # Julia binaries for ARM [Nightly builds](https://status.julialang.org/download/linux-arm) are -available for ARM. +available for ARMv7-A. + +# Hardware requirements + +Julia requires at least `armv6` and `vfpv2` instruction sets. It's recommended +to use at least `armv7-a`. `armv5` or soft float are not supported. # Building Julia on ARM diff --git a/src/codegen.cpp b/src/codegen.cpp index 31404db18925d..2ef5524333122 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -87,6 +87,7 @@ #if defined(_CPU_ARM_) || defined(_CPU_AARCH64_) # include +# include #endif #if defined(USE_POLLY) #include @@ -5562,10 +5563,81 @@ static void init_julia_llvm_env(Module *m) addOptimizationPasses(jl_globalPM); } +static inline std::string getNativeTarget() +{ + std::string cpu = sys::getHostCPUName(); +#if defined(_CPU_ARM_) + // Try slightly harder than LLVM at determine the CPU architecture. + if (cpu == "generic") { + // This is the most reliable way I can find + // `/proc/cpuinfo` changes between kernel versions + struct utsname name; + if (uname(&name) >= 0) { + // name.machine is the elf_platform in the kernel. + if (strcmp(name.machine, "armv6l") == 0) { + return "armv6"; + } + if (strcmp(name.machine, "armv7l") == 0) { + return "armv7"; + } + if (strcmp(name.machine, "armv7ml") == 0) { + // Thumb + return "armv7-m"; + } + if (strcmp(name.machine, "armv8l") == 0 || + strcmp(name.machine, "aarch64") == 0) { + return "armv8"; + } + } + } +#endif + return cpu; +} + +#if defined(_CPU_ARM_) || defined(_CPU_AARCH64_) +// Check if the cpu name is a ARM/AArch64 arch name and return a +// string that can be used as LLVM feature name +static inline void checkARMArchFeature(std::string &cpu, + StringMap &HostFeatures) +{ +#if defined(_CPU_ARM_) + if (cpu == "generic") { + HostFeatures["neon"] = false; + return; + } +#endif + StringRef cpu_s = cpu; + if (!cpu_s.startswith("armv")) + return; + // Generic names +#if defined(_CPU_ARM_) + if (!cpu_s.startswith("armv8")) { + // Turn off `neon` for generic archs on ARM + // since LLVM seems to enable it for all armv7-a processors. + HostFeatures["neon"] = false; + } + // "v7" and "v8" are not available in the form of `armv*` + // in the feature list + if (cpu == "armv7") { + HostFeatures["v7"] = true; + } + else if (cpu == "armv8") { + HostFeatures["v8"] = true; + } + else { + HostFeatures[cpu] = true; + } +#else + HostFeatures[cpu.substr(3)] = true; +#endif + cpu = "generic"; +} +#endif + // Helper to figure out what features to set for the LLVM target // If the user specifies native (or does not specify) we default // using the API provided by LLVM -static inline SmallVector getTargetFeatures() +static inline SmallVector getTargetFeatures(std::string &cpu) { StringMap HostFeatures; if (!strcmp(jl_options.cpu_target,"native")) { @@ -5594,22 +5666,88 @@ static inline SmallVector getTargetFeatures() #endif // Figure out if we know the cpu_target - std::string cpu = strcmp(jl_options.cpu_target,"native") ? jl_options.cpu_target : sys::getHostCPUName(); - if (cpu.empty() || cpu == "generic") { - jl_printf(JL_STDERR, "WARNING: unable to determine host cpu name.\n"); -#if defined(_CPU_ARM_) && defined(__ARM_PCS_VFP) - // Check if this is required when you have read the features directly from the processor - // This affects the platform calling convention. - // TODO: enable vfp3 for ARMv7+ (but adapt the ABI) - HostFeatures["vfp2"] = true; + cpu = (strcmp(jl_options.cpu_target,"native") ? jl_options.cpu_target : + getNativeTarget()); +#if defined(_CPU_ARM_) + // Figure out what we are compiling against from the C defines. + // This might affect ABI but is fine since + // 1. We define the C ABI explicitly. + // 2. This does not change when running the same binary on different + // machines. + // This shouldn't affect making generic binaries since that requires a + // generic C -march anyway. + HostFeatures["vfp2"] = true; + + // Arch version +#if __ARM_ARCH >= 8 +# if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'A' + HostFeatures["armv8-a"] = true; +# else + HostFeatures["v8"] = true; +# endif +#elif __ARM_ARCH >= 7 + // v7 + aclass emits slightly different code than armv7-a + // In particular LLVM does not use the armv7-a instruction for barrier + // with v7 + aclass. +# if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'A' + HostFeatures["armv7-a"] = true; +# elif defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'R' + HostFeatures["armv7-r"] = true; +# elif defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' + // Thumb + HostFeatures["armv7-m"] = true; +# else + HostFeatures["v7"] = true; +# endif +#else + // minimum requirement + HostFeatures["v6"] = true; +#endif + + // ARM profile + // Only do this on ARM and not AArch64 since LLVM aarch64 backend + // doesn't support setting profiles. + // AFAIK there's currently no 64bit R and M profile either + // (v8r and v8m are both 32bit) +#if defined(__ARM_ARCH_PROFILE) +# if __ARM_ARCH_PROFILE == 'A' + HostFeatures["aclass"] = true; +# elif __ARM_ARCH_PROFILE == 'R' + HostFeatures["rclass"] = true; +# elif __ARM_ARCH_PROFILE == 'M' + // Thumb + HostFeatures["mclass"] = true; +# endif +#endif +#endif // _CPU_ARM_ + + // On ARM and AArch64, allow using cpu_target to specify a CPU architecture + // which is specified in the feature set in LLVM. +#if defined(_CPU_ARM_) || defined(_CPU_AARCH64_) + // Supported ARM arch names on LLVM 3.8: + // armv6, armv6-m, armv6j, armv6k, armv6kz, armv6s-m, armv6t2, + // armv7, armv7-a, armv7-m, armv7-r, armv7e-m, armv7k, armv7s, + // armv8, armv8-a, armv8.1-a, armv8.2-a + // Additional ARM arch names on LLVM 3.9: + // armv8-m.base, armv8-m.main + // + // Supported AArch64 arch names on LLVM 3.8: + // armv8.1a, armv8.2a + checkARMArchFeature(cpu, HostFeatures); #endif - } SmallVector attr; - for (StringMap::const_iterator it = HostFeatures.begin(); it != HostFeatures.end(); it++) { - std::string att = it->getValue() ? it->getKey().str() : - std::string("-") + it->getKey().str(); - attr.append(1, att); + for (auto it = HostFeatures.begin(); it != HostFeatures.end(); it++) { + if (it->getValue()) { + attr.append(1, it->getKey().str()); + } + } + // Explicitly disabled features need to be added at the end so that + // they are not reenabled by other features that implies them by default. + for (auto it = HostFeatures.begin(); it != HostFeatures.end(); it++) { + if (!it->getValue()) { + attr.append(1, std::string("-") + it->getKey().str()); + } } return attr; } @@ -5720,8 +5858,8 @@ extern "C" void jl_init_codegen(void) TheTriple.setEnvironment(Triple::ELF); #endif #endif - std::string TheCPU = strcmp(jl_options.cpu_target,"native") ? jl_options.cpu_target : sys::getHostCPUName(); - SmallVector targetFeatures = getTargetFeatures( ); + std::string TheCPU; + SmallVector targetFeatures = getTargetFeatures(TheCPU); jl_TargetMachine = eb.selectTarget( TheTriple, "", diff --git a/src/disasm.cpp b/src/disasm.cpp index df3f15604e11b..356e57add0b9b 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -370,9 +370,7 @@ void jl_dump_asm_internal(uintptr_t Fptr, size_t Fsize, int64_t slide, { // GC safe // Get the host information - std::string TripleName; - if (TripleName.empty()) - TripleName = sys::getDefaultTargetTriple(); + std::string TripleName = sys::getDefaultTargetTriple(); Triple TheTriple(Triple::normalize(TripleName)); std::string MCPU = sys::getHostCPUName();