Index: llvm/trunk/include/llvm/Support/Host.h =================================================================== --- llvm/trunk/include/llvm/Support/Host.h +++ llvm/trunk/include/llvm/Support/Host.h @@ -70,6 +70,11 @@ /// /// \return - True on success. bool getHostCPUFeatures(StringMap &Features); + + /// Get the number of physical cores (as opposed to logical cores returned + /// from thread::hardware_concurrency(), which includes hyperthreads). + /// Returns -1 if unknown for the current host system. + int getHostNumPhysicalCores(); } } Index: llvm/trunk/include/llvm/Support/MemoryBuffer.h =================================================================== --- llvm/trunk/include/llvm/Support/MemoryBuffer.h +++ llvm/trunk/include/llvm/Support/MemoryBuffer.h @@ -70,6 +70,12 @@ getFile(const Twine &Filename, int64_t FileSize = -1, bool RequiresNullTerminator = true, bool IsVolatileSize = false); + /// Read all of the specified file into a MemoryBuffer as a stream + /// (i.e. until EOF reached). This is useful for special files that + /// look like a regular file but have 0 size (e.g. /proc/cpuinfo on Linux). + static ErrorOr> + getFileAsStream(const Twine &Filename); + /// Given an already-open file descriptor, map some slice of it into a /// MemoryBuffer. The slice is specified by an \p Offset and \p MapSize. /// Since this is in the middle of a file, the buffer is not null terminated. Index: llvm/trunk/lib/Support/Host.cpp =================================================================== --- llvm/trunk/lib/Support/Host.cpp +++ llvm/trunk/lib/Support/Host.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Host.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" @@ -19,9 +20,10 @@ #include "llvm/Config/config.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" -#include #include +#include // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX @@ -1188,6 +1190,60 @@ StringRef sys::getHostCPUName() { return "generic"; } #endif +#if defined(__linux__) && defined(__x86_64__) +// On Linux, the number of physical cores can be computed from /proc/cpuinfo, +// using the number of unique physical/core id pairs. The following +// implementation reads the /proc/cpuinfo format on an x86_64 system. +int computeHostNumPhysicalCores() { + // Read /proc/cpuinfo as a stream (until EOF reached). It cannot be + // mmapped because it appears to have 0 size. + llvm::ErrorOr> Text = + llvm::MemoryBuffer::getFileAsStream("/proc/cpuinfo"); + if (std::error_code EC = Text.getError()) { + llvm::errs() << "Can't read " + << "/proc/cpuinfo: " << EC.message() << "\n"; + } + SmallVector strs; + (*Text)->getBuffer().split(strs, "\n", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); + int CurPhysicalId = -1; + int CurCoreId = -1; + SmallSet, 32> UniqueItems; + for (auto &Line : strs) { + Line = Line.trim(); + if (!Line.startswith("physical id") && !Line.startswith("core id")) + continue; + std::pair Data = Line.split(':'); + auto Name = Data.first.trim(); + auto Val = Data.second.trim(); + if (Name == "physical id") { + assert(CurPhysicalId == -1 && + "Expected a core id before seeing another physical id"); + Val.getAsInteger(10, CurPhysicalId); + } + if (Name == "core id") { + assert(CurCoreId == -1 && + "Expected a physical id before seeing another core id"); + Val.getAsInteger(10, CurCoreId); + } + if (CurPhysicalId != -1 && CurCoreId != -1) { + UniqueItems.insert(std::make_pair(CurPhysicalId, CurCoreId)); + CurPhysicalId = -1; + CurCoreId = -1; + } + } + return UniqueItems.size(); +} +#else +// On other systems, return -1 to indicate unknown. +int computeHostNumPhysicalCores() { return -1; } +#endif + +int sys::getHostNumPhysicalCores() { + static int NumCores = computeHostNumPhysicalCores(); + return NumCores; +} + #if defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64__) || defined(_M_X64) bool sys::getHostCPUFeatures(StringMap &Features) { Index: llvm/trunk/lib/Support/MemoryBuffer.cpp =================================================================== --- llvm/trunk/lib/Support/MemoryBuffer.cpp +++ llvm/trunk/lib/Support/MemoryBuffer.cpp @@ -438,6 +438,18 @@ return getMemoryBufferForStream(0, ""); } +ErrorOr> +MemoryBuffer::getFileAsStream(const Twine &Filename) { + int FD; + std::error_code EC = sys::fs::openFileForRead(Filename, FD); + if (EC) + return EC; + ErrorOr> Ret = + getMemoryBufferForStream(FD, Filename); + close(FD); + return Ret; +} + MemoryBufferRef MemoryBuffer::getMemBufferRef() const { StringRef Data = getBuffer(); StringRef Identifier = getBufferIdentifier(); Index: llvm/trunk/unittests/Support/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/Support/CMakeLists.txt +++ llvm/trunk/unittests/Support/CMakeLists.txt @@ -19,6 +19,7 @@ ErrorTest.cpp ErrorOrTest.cpp FileOutputBufferTest.cpp + Host.cpp LEB128Test.cpp LineIteratorTest.cpp LockFileManagerTest.cpp Index: llvm/trunk/unittests/Support/Host.cpp =================================================================== --- llvm/trunk/unittests/Support/Host.cpp +++ llvm/trunk/unittests/Support/Host.cpp @@ -0,0 +1,47 @@ +//========- unittests/Support/Host.cpp - Host.cpp tests --------------========// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Host.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +class HostTest : public testing::Test { + Triple Host; + SmallVector, 4> SupportedArchAndOSs; + +protected: + bool isSupportedArchAndOS() { + if (is_contained(SupportedArchAndOSs, std::make_pair(Host.getArch(), Host.getOS()))) + return true; + + return false; + } + + HostTest() { + Host.setTriple(Triple::normalize(sys::getProcessTriple())); + + // Initially this is only testing detection of the number of + // physical cores, which is currently only supported for + // x86_64 Linux. + SupportedArchAndOSs.push_back(std::make_pair(Triple::x86_64, Triple::Linux)); + } +}; + +TEST_F(HostTest, NumPhysicalCores) { + int Num = sys::getHostNumPhysicalCores(); + + if (isSupportedArchAndOS()) + ASSERT_GT(Num, 0); + else + ASSERT_EQ(Num, -1); +}