Skip to content

Commit e284c6d

Browse files
yuyichaostaticfloat
authored andcommitted
Improve arch/cpu detection/selection on ARM and AArch64
* Allow `cpu_target` to specify a generic arch, matching the behavior on x86 * Detect the CPU arch version with `uname` * Require `armv6` Close #13270 (`armv5` is not supported) Fix #18042 * Remove warning about generic arch since it's not really useful Fix #17549 * Require at least the same ARM arch version and profile the C code is compiled with (cherry picked from commit d9f5334)
1 parent 6445c82 commit e284c6d

File tree

3 files changed

+125
-16
lines changed

3 files changed

+125
-16
lines changed

README.arm.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
# Julia binaries for ARM
22

33
[Nightly builds](https://status.julialang.org/download/linux-arm) are
4-
available for ARM.
4+
available for ARMv7-A.
5+
6+
# Hardware requirements
7+
8+
Julia requires at least `armv6` and `vfpv2` instruction sets. It's recommanded
9+
to use at least `armv7-a`. `armv5` or soft float are not supported.
510

611
# Building Julia on ARM
712

src/codegen.cpp

Lines changed: 118 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787

8888
#if defined(_CPU_ARM_) || defined(_CPU_AARCH64_)
8989
# include <llvm/IR/InlineAsm.h>
90+
# include <sys/utsname.h>
9091
#endif
9192
#if defined(USE_POLLY)
9293
#include <polly/RegisterPasses.h>
@@ -5562,10 +5563,68 @@ static void init_julia_llvm_env(Module *m)
55625563
addOptimizationPasses(jl_globalPM);
55635564
}
55645565

5566+
static inline std::string getNativeTarget()
5567+
{
5568+
std::string cpu = sys::getHostCPUName();
5569+
#if defined(_CPU_ARM_)
5570+
// Try slightly harder than LLVM at determine the CPU architecture.
5571+
if (cpu == "generic") {
5572+
// This is the most reliable way I can find
5573+
// `/proc/cpuinfo` changes between kernel versions
5574+
struct utsname name;
5575+
if (uname(&name) >= 0) {
5576+
// name.machine is the elf_platform in the kernel.
5577+
if (strcmp(name.machine, "armv6l") == 0) {
5578+
return "armv6";
5579+
}
5580+
if (strcmp(name.machine, "armv7l") == 0) {
5581+
return "armv7";
5582+
}
5583+
if (strcmp(name.machine, "armv7ml") == 0) {
5584+
// Thumb
5585+
return "armv7-m";
5586+
}
5587+
if (strcmp(name.machine, "armv8l") == 0 ||
5588+
strcmp(name.machine, "aarch64") == 0) {
5589+
return "armv8";
5590+
}
5591+
}
5592+
}
5593+
#endif
5594+
return cpu;
5595+
}
5596+
5597+
#if defined(_CPU_ARM_) || defined(_CPU_AARCH64_)
5598+
// Check if the cpu name is a ARM/AArch64 arch name and return a
5599+
// string that can be used as LLVM feature name
5600+
static inline std::string checkARMArchFeature(const std::string &cpu)
5601+
{
5602+
const char *prefix = "armv";
5603+
size_t prefix_len = strlen(prefix);
5604+
if (cpu.size() <= prefix_len ||
5605+
memcmp(cpu.data(), prefix, prefix_len) != 0 ||
5606+
cpu[prefix_len] < '1' || cpu[prefix_len] > '9')
5607+
return std::string();
5608+
#if defined(_CPU_ARM_)
5609+
// "v7" and "v8" are not available in the form of `armv*`
5610+
// in the feature list
5611+
if (cpu == "armv7") {
5612+
return "v7";
5613+
}
5614+
else if (cpu == "armv8") {
5615+
return "v8";
5616+
}
5617+
return cpu;
5618+
#else
5619+
return cpu.substr(3);
5620+
#endif
5621+
}
5622+
#endif
5623+
55655624
// Helper to figure out what features to set for the LLVM target
55665625
// If the user specifies native (or does not specify) we default
55675626
// using the API provided by LLVM
5568-
static inline SmallVector<std::string,10> getTargetFeatures()
5627+
static inline SmallVector<std::string,10> getTargetFeatures(std::string &cpu)
55695628
{
55705629
StringMap<bool> HostFeatures;
55715630
if (!strcmp(jl_options.cpu_target,"native")) {
@@ -5594,16 +5653,63 @@ static inline SmallVector<std::string,10> getTargetFeatures()
55945653
#endif
55955654

55965655
// Figure out if we know the cpu_target
5597-
std::string cpu = strcmp(jl_options.cpu_target,"native") ? jl_options.cpu_target : sys::getHostCPUName();
5598-
if (cpu.empty() || cpu == "generic") {
5599-
jl_printf(JL_STDERR, "WARNING: unable to determine host cpu name.\n");
5600-
#if defined(_CPU_ARM_) && defined(__ARM_PCS_VFP)
5601-
// Check if this is required when you have read the features directly from the processor
5602-
// This affects the platform calling convention.
5603-
// TODO: enable vfp3 for ARMv7+ (but adapt the ABI)
5604-
HostFeatures["vfp2"] = true;
5605-
#endif
5656+
cpu = (strcmp(jl_options.cpu_target,"native") ? jl_options.cpu_target :
5657+
getNativeTarget());
5658+
#if defined(_CPU_ARM_)
5659+
// Figure out what we are compiling against from the C defines.
5660+
// This might affect ABI but is fine since
5661+
// 1. We define the C ABI explicitly.
5662+
// 2. This does not change when running the same binary on different
5663+
// machines.
5664+
// This shouldn't affect making generic binaries since that requires a
5665+
// generic C -march anyway.
5666+
HostFeatures["vfp2"] = true;
5667+
5668+
// Arch version
5669+
#if __ARM_ARCH >= 8
5670+
HostFeatures["v8"] = true;
5671+
#elif __ARM_ARCH >= 7
5672+
HostFeatures["v7"] = true;
5673+
#else
5674+
// minimum requirement
5675+
HostFeatures["v6"] = true;
5676+
#endif
5677+
5678+
// ARM profile
5679+
// Only do this on ARM and not AArch64 since LLVM aarch64 backend
5680+
// doesn't support setting profiles.
5681+
// AFAIK there's currently no 64bit R and M profile either
5682+
// (v8r and v8m are both 32bit)
5683+
#if defined(__ARM_ARCH_PROFILE)
5684+
# if __ARM_ARCH_PROFILE == 'A'
5685+
HostFeatures["aclass"] = true;
5686+
# elif __ARM_ARCH_PROFILE == 'R'
5687+
HostFeatures["rclass"] = true;
5688+
# elif __ARM_ARCH_PROFILE == 'M'
5689+
// Thumb
5690+
HostFeatures["mclass"] = true;
5691+
# endif
5692+
#endif
5693+
#endif // _CPU_ARM_
5694+
5695+
// On ARM and AArch64, allow using cpu_target to specify a CPU architecture
5696+
// which is specified in the feature set in LLVM.
5697+
#if defined(_CPU_ARM_) || defined(_CPU_AARCH64_)
5698+
// Supported ARM arch names on LLVM 3.8:
5699+
// armv6, armv6-m, armv6j, armv6k, armv6kz, armv6s-m, armv6t2,
5700+
// armv7, armv7-a, armv7-m, armv7-r, armv7e-m, armv7k, armv7s,
5701+
// armv8, armv8-a, armv8.1-a, armv8.2-a
5702+
// Additional ARM arch names on LLVM 3.9:
5703+
// armv8-m.base, armv8-m.main
5704+
//
5705+
// Supported AArch64 arch names on LLVM 3.8:
5706+
// armv8.1a, armv8.2a
5707+
std::string arm_arch = checkARMArchFeature(cpu);
5708+
if (!arm_arch.empty()) {
5709+
HostFeatures[arm_arch] = true;
5710+
cpu = "generic";
56065711
}
5712+
#endif
56075713

56085714
SmallVector<std::string,10> attr;
56095715
for (StringMap<bool>::const_iterator it = HostFeatures.begin(); it != HostFeatures.end(); it++) {
@@ -5720,8 +5826,8 @@ extern "C" void jl_init_codegen(void)
57205826
TheTriple.setEnvironment(Triple::ELF);
57215827
#endif
57225828
#endif
5723-
std::string TheCPU = strcmp(jl_options.cpu_target,"native") ? jl_options.cpu_target : sys::getHostCPUName();
5724-
SmallVector<std::string, 10> targetFeatures = getTargetFeatures( );
5829+
std::string TheCPU;
5830+
SmallVector<std::string, 10> targetFeatures = getTargetFeatures(TheCPU);
57255831
jl_TargetMachine = eb.selectTarget(
57265832
TheTriple,
57275833
"",

src/disasm.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,7 @@ void jl_dump_asm_internal(uintptr_t Fptr, size_t Fsize, int64_t slide,
370370
{
371371
// GC safe
372372
// Get the host information
373-
std::string TripleName;
374-
if (TripleName.empty())
375-
TripleName = sys::getDefaultTargetTriple();
373+
std::string TripleName = sys::getDefaultTargetTriple();
376374
Triple TheTriple(Triple::normalize(TripleName));
377375

378376
std::string MCPU = sys::getHostCPUName();

0 commit comments

Comments
 (0)