summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristof Beyls <kristof.beyls@arm.com>2017-03-30 07:24:49 +0000
committerKristof Beyls <kristof.beyls@arm.com>2017-03-30 07:24:49 +0000
commit9e46396ecc0e91110252a02c79ce775c83643961 (patch)
tree08540f366dc1ccb2dcca6797072a8c8159f2ccb2
parenteafcbe2d1069f32c80054c91af6862c0ed93696e (diff)
downloadbcm5719-llvm-9e46396ecc0e91110252a02c79ce775c83643961.tar.gz
bcm5719-llvm-9e46396ecc0e91110252a02c79ce775c83643961.zip
Refactor getHostCPUName to allow testing on non-native hardware.
This refactors getHostCPUName so that for the architectures that get the host cpu info on linux from /proc/cpuinfo, the /proc/cpuinfo parsing logic is present in the build, even if it wasn't built on a linux system for that architecture. Since the code is present in the build, we can then test that code also on other systems, i.e. we don't need to have buildbots setup for all architectures on linux to be able to test this. Instead, developers will test this as part of the regression test run. As an example, a few unit tests are added to test getHostCPUName for ARM running linux. A unit test is preferred over a lit-based test, since the expectation is that in the future, the functionality here will grow over what can be tested with "llc -mcpu=native". This is a preparation step to enable implementing the range of improvements discussed on PR30516, such as adding AArch64 support, support for big.LITTLE systems, reducing code duplication. Differential Revision: https://reviews.llvm.org/D31236 llvm-svn: 299060
-rw-r--r--llvm/include/llvm/Support/Host.h8
-rw-r--r--llvm/lib/Support/Host.cpp418
-rw-r--r--llvm/unittests/Support/Host.cpp44
3 files changed, 256 insertions, 214 deletions
diff --git a/llvm/include/llvm/Support/Host.h b/llvm/include/llvm/Support/Host.h
index 878debde605..22a61f76064 100644
--- a/llvm/include/llvm/Support/Host.h
+++ b/llvm/include/llvm/Support/Host.h
@@ -15,6 +15,7 @@
#define LLVM_SUPPORT_HOST_H
#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/MemoryBuffer.h"
#if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__)
#include <endian.h>
@@ -75,6 +76,13 @@ constexpr bool IsBigEndianHost = false;
/// from thread::hardware_concurrency(), which includes hyperthreads).
/// Returns -1 if unknown for the current host system.
int getHostNumPhysicalCores();
+
+ /// helper functions to extract HostCPUName from /proc/cpuinfo on linux.
+ namespace LinuxReadCpuInfo {
+ StringRef getHostCPUName_powerpc(const StringRef &ProcCpuinfoContent);
+ StringRef getHostCPUName_arm(const StringRef &ProcCpuinfoContent);
+ StringRef getHostCPUName_s390x(const StringRef &ProcCpuinfoContent);
+ }
}
}
diff --git a/llvm/lib/Support/Host.cpp b/llvm/lib/Support/Host.cpp
index a87399380a7..230d0921184 100644
--- a/llvm/lib/Support/Host.cpp
+++ b/llvm/lib/Support/Host.cpp
@@ -52,25 +52,200 @@
using namespace llvm;
-#if defined(__linux__)
-static ssize_t LLVM_ATTRIBUTE_UNUSED readCpuInfo(void *Buf, size_t Size) {
- // Note: We cannot mmap /proc/cpuinfo here and then process the resulting
- // memory buffer because the 'file' has 0 size (it can be read from only
- // as a stream).
-
- int FD;
- std::error_code EC = sys::fs::openFileForRead("/proc/cpuinfo", FD);
- if (EC) {
- DEBUG(dbgs() << "Unable to open /proc/cpuinfo: " << EC.message() << "\n");
- return -1;
+static std::unique_ptr<llvm::MemoryBuffer>
+ LLVM_ATTRIBUTE_UNUSED getProcCpuinfoContent() {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
+ llvm::MemoryBuffer::getFileAsStream("/proc/cpuinfo");
+ if (std::error_code EC = Text.getError()) {
+ llvm::errs() << "Can't read "
+ << "/proc/cpuinfo: " << EC.message() << "\n";
+ return nullptr;
}
- int Ret = read(FD, Buf, Size);
- int CloseStatus = close(FD);
- if (CloseStatus)
- return -1;
- return Ret;
+ return std::move(*Text);
+}
+
+StringRef sys::LinuxReadCpuInfo::getHostCPUName_powerpc(
+ const StringRef &ProcCpuinfoContent) {
+ // Access to the Processor Version Register (PVR) on PowerPC is privileged,
+ // and so we must use an operating-system interface to determine the current
+ // processor type. On Linux, this is exposed through the /proc/cpuinfo file.
+ const char *generic = "generic";
+
+ // The cpu line is second (after the 'processor: 0' line), so if this
+ // buffer is too small then something has changed (or is wrong).
+ StringRef::const_iterator CPUInfoStart = ProcCpuinfoContent.begin();
+ StringRef::const_iterator CPUInfoEnd = ProcCpuinfoContent.end();
+
+ StringRef::const_iterator CIP = CPUInfoStart;
+
+ StringRef::const_iterator CPUStart = 0;
+ size_t CPULen = 0;
+
+ // We need to find the first line which starts with cpu, spaces, and a colon.
+ // After the colon, there may be some additional spaces and then the cpu type.
+ while (CIP < CPUInfoEnd && CPUStart == 0) {
+ if (CIP < CPUInfoEnd && *CIP == '\n')
+ ++CIP;
+
+ if (CIP < CPUInfoEnd && *CIP == 'c') {
+ ++CIP;
+ if (CIP < CPUInfoEnd && *CIP == 'p') {
+ ++CIP;
+ if (CIP < CPUInfoEnd && *CIP == 'u') {
+ ++CIP;
+ while (CIP < CPUInfoEnd && (*CIP == ' ' || *CIP == '\t'))
+ ++CIP;
+
+ if (CIP < CPUInfoEnd && *CIP == ':') {
+ ++CIP;
+ while (CIP < CPUInfoEnd && (*CIP == ' ' || *CIP == '\t'))
+ ++CIP;
+
+ if (CIP < CPUInfoEnd) {
+ CPUStart = CIP;
+ while (CIP < CPUInfoEnd && (*CIP != ' ' && *CIP != '\t' &&
+ *CIP != ',' && *CIP != '\n'))
+ ++CIP;
+ CPULen = CIP - CPUStart;
+ }
+ }
+ }
+ }
+ }
+
+ if (CPUStart == 0)
+ while (CIP < CPUInfoEnd && *CIP != '\n')
+ ++CIP;
+ }
+
+ if (CPUStart == 0)
+ return generic;
+
+ return StringSwitch<const char *>(StringRef(CPUStart, CPULen))
+ .Case("604e", "604e")
+ .Case("604", "604")
+ .Case("7400", "7400")
+ .Case("7410", "7400")
+ .Case("7447", "7400")
+ .Case("7455", "7450")
+ .Case("G4", "g4")
+ .Case("POWER4", "970")
+ .Case("PPC970FX", "970")
+ .Case("PPC970MP", "970")
+ .Case("G5", "g5")
+ .Case("POWER5", "g5")
+ .Case("A2", "a2")
+ .Case("POWER6", "pwr6")
+ .Case("POWER7", "pwr7")
+ .Case("POWER8", "pwr8")
+ .Case("POWER8E", "pwr8")
+ .Case("POWER8NVL", "pwr8")
+ .Case("POWER9", "pwr9")
+ .Default(generic);
+}
+
+StringRef sys::LinuxReadCpuInfo::getHostCPUName_arm(
+ const StringRef &ProcCpuinfoContent) {
+ // The cpuid register on arm is not accessible from user space. On Linux,
+ // it is exposed through the /proc/cpuinfo file.
+
+ // Read 1024 bytes from /proc/cpuinfo, which should contain the CPU part line
+ // in all cases.
+ SmallVector<StringRef, 32> Lines;
+ ProcCpuinfoContent.split(Lines, "\n");
+
+ // Look for the CPU implementer line.
+ StringRef Implementer;
+ for (unsigned I = 0, E = Lines.size(); I != E; ++I)
+ if (Lines[I].startswith("CPU implementer"))
+ Implementer = Lines[I].substr(15).ltrim("\t :");
+
+ if (Implementer == "0x41") // ARM Ltd.
+ // Look for the CPU part line.
+ for (unsigned I = 0, E = Lines.size(); I != E; ++I)
+ if (Lines[I].startswith("CPU part"))
+ // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The
+ // values correspond to the "Part number" in the CP15/c0 register. The
+ // contents are specified in the various processor manuals.
+ return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :"))
+ .Case("0x926", "arm926ej-s")
+ .Case("0xb02", "mpcore")
+ .Case("0xb36", "arm1136j-s")
+ .Case("0xb56", "arm1156t2-s")
+ .Case("0xb76", "arm1176jz-s")
+ .Case("0xc08", "cortex-a8")
+ .Case("0xc09", "cortex-a9")
+ .Case("0xc0f", "cortex-a15")
+ .Case("0xc20", "cortex-m0")
+ .Case("0xc23", "cortex-m3")
+ .Case("0xc24", "cortex-m4")
+ .Default("generic");
+
+ if (Implementer == "0x51") // Qualcomm Technologies, Inc.
+ // Look for the CPU part line.
+ for (unsigned I = 0, E = Lines.size(); I != E; ++I)
+ if (Lines[I].startswith("CPU part"))
+ // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The
+ // values correspond to the "Part number" in the CP15/c0 register. The
+ // contents are specified in the various processor manuals.
+ return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :"))
+ .Case("0x06f", "krait") // APQ8064
+ .Default("generic");
+
+ return "generic";
+}
+
+StringRef sys::LinuxReadCpuInfo::getHostCPUName_s390x(
+ const StringRef &ProcCpuinfoContent) {
+ // STIDP is a privileged operation, so use /proc/cpuinfo instead.
+
+ // The "processor 0:" line comes after a fair amount of other information,
+ // including a cache breakdown, but this should be plenty.
+ SmallVector<StringRef, 32> Lines;
+ ProcCpuinfoContent.split(Lines, "\n");
+
+ // Look for the CPU features.
+ SmallVector<StringRef, 32> CPUFeatures;
+ for (unsigned I = 0, E = Lines.size(); I != E; ++I)
+ if (Lines[I].startswith("features")) {
+ size_t Pos = Lines[I].find(":");
+ if (Pos != StringRef::npos) {
+ Lines[I].drop_front(Pos + 1).split(CPUFeatures, ' ');
+ break;
+ }
+ }
+
+ // We need to check for the presence of vector support independently of
+ // the machine type, since we may only use the vector register set when
+ // supported by the kernel (and hypervisor).
+ bool HaveVectorSupport = false;
+ for (unsigned I = 0, E = CPUFeatures.size(); I != E; ++I) {
+ if (CPUFeatures[I] == "vx")
+ HaveVectorSupport = true;
+ }
+
+ // Now check the processor machine type.
+ for (unsigned I = 0, E = Lines.size(); I != E; ++I) {
+ if (Lines[I].startswith("processor ")) {
+ size_t Pos = Lines[I].find("machine = ");
+ if (Pos != StringRef::npos) {
+ Pos += sizeof("machine = ") - 1;
+ unsigned int Id;
+ if (!Lines[I].drop_front(Pos).getAsInteger(10, Id)) {
+ if (Id >= 2964 && HaveVectorSupport)
+ return "z13";
+ if (Id >= 2827)
+ return "zEC12";
+ if (Id >= 2817)
+ return "z196";
+ }
+ }
+ break;
+ }
+ }
+
+ return "generic";
}
-#endif
#if defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64__) || defined(_M_X64)
@@ -1020,201 +1195,21 @@ StringRef sys::getHostCPUName() {
}
#elif defined(__linux__) && (defined(__ppc__) || defined(__powerpc__))
StringRef sys::getHostCPUName() {
- // Access to the Processor Version Register (PVR) on PowerPC is privileged,
- // and so we must use an operating-system interface to determine the current
- // processor type. On Linux, this is exposed through the /proc/cpuinfo file.
- const char *generic = "generic";
-
- // The cpu line is second (after the 'processor: 0' line), so if this
- // buffer is too small then something has changed (or is wrong).
- char buffer[1024];
- ssize_t CPUInfoSize = readCpuInfo(buffer, sizeof(buffer));
- if (CPUInfoSize == -1)
- return generic;
-
- const char *CPUInfoStart = buffer;
- const char *CPUInfoEnd = buffer + CPUInfoSize;
-
- const char *CIP = CPUInfoStart;
-
- const char *CPUStart = 0;
- size_t CPULen = 0;
-
- // We need to find the first line which starts with cpu, spaces, and a colon.
- // After the colon, there may be some additional spaces and then the cpu type.
- while (CIP < CPUInfoEnd && CPUStart == 0) {
- if (CIP < CPUInfoEnd && *CIP == '\n')
- ++CIP;
-
- if (CIP < CPUInfoEnd && *CIP == 'c') {
- ++CIP;
- if (CIP < CPUInfoEnd && *CIP == 'p') {
- ++CIP;
- if (CIP < CPUInfoEnd && *CIP == 'u') {
- ++CIP;
- while (CIP < CPUInfoEnd && (*CIP == ' ' || *CIP == '\t'))
- ++CIP;
-
- if (CIP < CPUInfoEnd && *CIP == ':') {
- ++CIP;
- while (CIP < CPUInfoEnd && (*CIP == ' ' || *CIP == '\t'))
- ++CIP;
-
- if (CIP < CPUInfoEnd) {
- CPUStart = CIP;
- while (CIP < CPUInfoEnd && (*CIP != ' ' && *CIP != '\t' &&
- *CIP != ',' && *CIP != '\n'))
- ++CIP;
- CPULen = CIP - CPUStart;
- }
- }
- }
- }
- }
-
- if (CPUStart == 0)
- while (CIP < CPUInfoEnd && *CIP != '\n')
- ++CIP;
- }
-
- if (CPUStart == 0)
- return generic;
-
- return StringSwitch<const char *>(StringRef(CPUStart, CPULen))
- .Case("604e", "604e")
- .Case("604", "604")
- .Case("7400", "7400")
- .Case("7410", "7400")
- .Case("7447", "7400")
- .Case("7455", "7450")
- .Case("G4", "g4")
- .Case("POWER4", "970")
- .Case("PPC970FX", "970")
- .Case("PPC970MP", "970")
- .Case("G5", "g5")
- .Case("POWER5", "g5")
- .Case("A2", "a2")
- .Case("POWER6", "pwr6")
- .Case("POWER7", "pwr7")
- .Case("POWER8", "pwr8")
- .Case("POWER8E", "pwr8")
- .Case("POWER8NVL", "pwr8")
- .Case("POWER9", "pwr9")
- .Default(generic);
+ std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent();
+ const StringRef& Content = P ? P->getBuffer() : "";
+ return LinuxReadCpuInfo::getHostCPUName_powerpc(Content);
}
#elif defined(__linux__) && defined(__arm__)
StringRef sys::getHostCPUName() {
- // The cpuid register on arm is not accessible from user space. On Linux,
- // it is exposed through the /proc/cpuinfo file.
-
- // Read 1024 bytes from /proc/cpuinfo, which should contain the CPU part line
- // in all cases.
- char buffer[1024];
- ssize_t CPUInfoSize = readCpuInfo(buffer, sizeof(buffer));
- if (CPUInfoSize == -1)
- return "generic";
-
- StringRef Str(buffer, CPUInfoSize);
-
- SmallVector<StringRef, 32> Lines;
- Str.split(Lines, "\n");
-
- // Look for the CPU implementer line.
- StringRef Implementer;
- for (unsigned I = 0, E = Lines.size(); I != E; ++I)
- if (Lines[I].startswith("CPU implementer"))
- Implementer = Lines[I].substr(15).ltrim("\t :");
-
- if (Implementer == "0x41") // ARM Ltd.
- // Look for the CPU part line.
- for (unsigned I = 0, E = Lines.size(); I != E; ++I)
- if (Lines[I].startswith("CPU part"))
- // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The
- // values correspond to the "Part number" in the CP15/c0 register. The
- // contents are specified in the various processor manuals.
- return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :"))
- .Case("0x926", "arm926ej-s")
- .Case("0xb02", "mpcore")
- .Case("0xb36", "arm1136j-s")
- .Case("0xb56", "arm1156t2-s")
- .Case("0xb76", "arm1176jz-s")
- .Case("0xc08", "cortex-a8")
- .Case("0xc09", "cortex-a9")
- .Case("0xc0f", "cortex-a15")
- .Case("0xc20", "cortex-m0")
- .Case("0xc23", "cortex-m3")
- .Case("0xc24", "cortex-m4")
- .Default("generic");
-
- if (Implementer == "0x51") // Qualcomm Technologies, Inc.
- // Look for the CPU part line.
- for (unsigned I = 0, E = Lines.size(); I != E; ++I)
- if (Lines[I].startswith("CPU part"))
- // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The
- // values correspond to the "Part number" in the CP15/c0 register. The
- // contents are specified in the various processor manuals.
- return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :"))
- .Case("0x06f", "krait") // APQ8064
- .Default("generic");
-
- return "generic";
+ std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent();
+ const StringRef& Content = P ? P->getBuffer() : "";
+ return LinuxReadCpuInfo::getHostCPUName_arm(Content);
}
#elif defined(__linux__) && defined(__s390x__)
StringRef sys::getHostCPUName() {
- // STIDP is a privileged operation, so use /proc/cpuinfo instead.
-
- // The "processor 0:" line comes after a fair amount of other information,
- // including a cache breakdown, but this should be plenty.
- char buffer[2048];
- ssize_t CPUInfoSize = readCpuInfo(buffer, sizeof(buffer));
- if (CPUInfoSize == -1)
- return "generic";
-
- StringRef Str(buffer, CPUInfoSize);
- SmallVector<StringRef, 32> Lines;
- Str.split(Lines, "\n");
-
- // Look for the CPU features.
- SmallVector<StringRef, 32> CPUFeatures;
- for (unsigned I = 0, E = Lines.size(); I != E; ++I)
- if (Lines[I].startswith("features")) {
- size_t Pos = Lines[I].find(":");
- if (Pos != StringRef::npos) {
- Lines[I].drop_front(Pos + 1).split(CPUFeatures, ' ');
- break;
- }
- }
-
- // We need to check for the presence of vector support independently of
- // the machine type, since we may only use the vector register set when
- // supported by the kernel (and hypervisor).
- bool HaveVectorSupport = false;
- for (unsigned I = 0, E = CPUFeatures.size(); I != E; ++I) {
- if (CPUFeatures[I] == "vx")
- HaveVectorSupport = true;
- }
-
- // Now check the processor machine type.
- for (unsigned I = 0, E = Lines.size(); I != E; ++I) {
- if (Lines[I].startswith("processor ")) {
- size_t Pos = Lines[I].find("machine = ");
- if (Pos != StringRef::npos) {
- Pos += sizeof("machine = ") - 1;
- unsigned int Id;
- if (!Lines[I].drop_front(Pos).getAsInteger(10, Id)) {
- if (Id >= 2964 && HaveVectorSupport)
- return "z13";
- if (Id >= 2827)
- return "zEC12";
- if (Id >= 2817)
- return "z196";
- }
- }
- break;
- }
- }
-
- return "generic";
+ std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent();
+ const StringRef& Content = P ? P->getBuffer() : "";
+ return LinuxReadCpuInfo::getHostCPUName_s390x(Content);
}
#else
StringRef sys::getHostCPUName() { return "generic"; }
@@ -1401,17 +1396,12 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) {
}
#elif defined(__linux__) && (defined(__arm__) || defined(__aarch64__))
bool sys::getHostCPUFeatures(StringMap<bool> &Features) {
- // Read 1024 bytes from /proc/cpuinfo, which should contain the Features line
- // in all cases.
- char buffer[1024];
- ssize_t CPUInfoSize = readCpuInfo(buffer, sizeof(buffer));
- if (CPUInfoSize == -1)
+ std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent();
+ if (!P)
return false;
- StringRef Str(buffer, CPUInfoSize);
-
SmallVector<StringRef, 32> Lines;
- Str.split(Lines, "\n");
+ P->getBuffer().split(Lines, "\n");
SmallVector<StringRef, 32> CPUFeatures;
diff --git a/llvm/unittests/Support/Host.cpp b/llvm/unittests/Support/Host.cpp
index 10f883e6d2c..0d22ba8ca79 100644
--- a/llvm/unittests/Support/Host.cpp
+++ b/llvm/unittests/Support/Host.cpp
@@ -38,3 +38,47 @@ TEST_F(HostTest, NumPhysicalCores) {
else
ASSERT_EQ(Num, -1);
}
+
+TEST(getLinuxHostCPUName, ARM) {
+ StringRef CortexA9ProcCpuinfo = R"(
+processor : 0
+model name : ARMv7 Processor rev 10 (v7l)
+BogoMIPS : 1393.66
+Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x2
+CPU part : 0xc09
+CPU revision : 10
+
+processor : 1
+model name : ARMv7 Processor rev 10 (v7l)
+BogoMIPS : 1393.66
+Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x2
+CPU part : 0xc09
+CPU revision : 10
+
+Hardware : Generic OMAP4 (Flattened Device Tree)
+Revision : 0000
+Serial : 0000000000000000
+)";
+
+ EXPECT_EQ(sys::LinuxReadCpuInfo::getHostCPUName_arm(CortexA9ProcCpuinfo),
+ "cortex-a9");
+ EXPECT_EQ(
+ sys::LinuxReadCpuInfo::getHostCPUName_arm("CPU implementer : 0x41\n"
+ "CPU part : 0xc0f"),
+ "cortex-a15");
+ // Verify that both CPU implementer and CPU part are checked:
+ EXPECT_EQ(
+ sys::LinuxReadCpuInfo::getHostCPUName_arm("CPU implementer : 0x40\n"
+ "CPU part : 0xc0f"),
+ "generic");
+ EXPECT_EQ(
+ sys::LinuxReadCpuInfo::getHostCPUName_arm("CPU implementer : 0x51\n"
+ "CPU part : 0x06f"),
+ "krait");
+}
OpenPOWER on IntegriCloud