Skip to content

[release-0.5] Backport #19022 #21708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.arm.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
170 changes: 154 additions & 16 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@

#if defined(_CPU_ARM_) || defined(_CPU_AARCH64_)
# include <llvm/IR/InlineAsm.h>
# include <sys/utsname.h>
#endif
#if defined(USE_POLLY)
#include <polly/RegisterPasses.h>
Expand Down Expand Up @@ -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<bool> &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<std::string,10> getTargetFeatures()
static inline SmallVector<std::string,10> getTargetFeatures(std::string &cpu)
{
StringMap<bool> HostFeatures;
if (!strcmp(jl_options.cpu_target,"native")) {
Expand Down Expand Up @@ -5594,22 +5666,88 @@ static inline SmallVector<std::string,10> 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<std::string,10> attr;
for (StringMap<bool>::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;
}
Expand Down Expand Up @@ -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<std::string, 10> targetFeatures = getTargetFeatures( );
std::string TheCPU;
SmallVector<std::string, 10> targetFeatures = getTargetFeatures(TheCPU);
jl_TargetMachine = eb.selectTarget(
TheTriple,
"",
Expand Down
4 changes: 1 addition & 3 deletions src/disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down