[CUDA] Improve CUDA version detection and diagnostics.

Always use cuda.h to detect CUDA version. It's a more universal approach
compared to version.txt which is no longer present in recent CUDA versions.

Split the 'unknown CUDA version' warning in two:

* when detected CUDA version is partially supported by clang. It's expected to
work in general, at the feature parity with the latest supported CUDA
version. and may be missing support for the new features/instructions/GPU
variants. Clang will issue a warning.

* when detected version is new. Recent CUDA versions have been working with
clang reasonably well, and will likely to work similarly to the partially
supported ones above. Or it may not work at all. Clang will issue a warning and
proceed as if the latest known CUDA version was detected.

Differential Revision: https://reviews.llvm.org/D108247
This commit is contained in:
Artem Belevich 2021-08-17 12:32:05 -07:00
parent 49d982d8cb
commit 3db8e486e5
16 changed files with 71 additions and 71 deletions

View File

@ -33,8 +33,10 @@ enum class CudaVersion {
CUDA_112,
CUDA_113,
CUDA_114,
LATEST = CUDA_114,
LATEST_SUPPORTED = CUDA_101,
FULLY_SUPPORTED = CUDA_101,
PARTIALLY_SUPPORTED =
CUDA_114, // Partially supported. Proceed with a warning.
NEW = 10000, // Too new. Issue a warning, but allow using it.
};
const char *CudaVersionToString(CudaVersion V);
// Input is "Major.Minor"

View File

@ -77,8 +77,11 @@ def err_drv_cuda_version_unsupported : Error<
"but installation at %3 is %4; use '--cuda-path' to specify a different CUDA "
"install, pass a different GPU arch with '--cuda-gpu-arch', or pass "
"'--no-cuda-version-check'">;
def warn_drv_unknown_cuda_version: Warning<
"unknown CUDA version: %0; assuming the latest supported version %1">,
def warn_drv_new_cuda_version: Warning<
"CUDA version%0 is newer than the latest%select{| partially}1 supported version %2">,
InGroup<CudaUnknownVersion>;
def warn_drv_partially_supported_cuda_version: Warning<
"CUDA version %0 is only partially supported">,
InGroup<CudaUnknownVersion>;
def err_drv_cuda_host_arch : Error<
"unsupported architecture '%0' for host compilation">;

View File

@ -40,6 +40,8 @@ const char *CudaVersionToString(CudaVersion V) {
return "11.3";
case CudaVersion::CUDA_114:
return "11.4";
case CudaVersion::NEW:
return "";
}
llvm_unreachable("invalid enum");
}
@ -192,7 +194,7 @@ CudaVersion MinVersionForCudaArch(CudaArch A) {
CudaVersion MaxVersionForCudaArch(CudaArch A) {
// AMD GPUs do not depend on CUDA versions.
if (IsAMDGpuArch(A))
return CudaVersion::LATEST;
return CudaVersion::NEW;
switch (A) {
case CudaArch::UNKNOWN:
@ -203,7 +205,7 @@ CudaVersion MaxVersionForCudaArch(CudaArch A) {
case CudaArch::SM_30:
return CudaVersion::CUDA_110;
default:
return CudaVersion::LATEST;
return CudaVersion::NEW;
}
}

View File

@ -17,6 +17,7 @@
#include "clang/Driver/InputInfo.h"
#include "clang/Driver/Options.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
@ -34,25 +35,6 @@ using namespace clang;
using namespace llvm::opt;
namespace {
struct CudaVersionInfo {
std::string DetectedVersion;
CudaVersion Version;
};
// Parses the contents of version.txt in an CUDA installation. It should
// contain one line of the from e.g. "CUDA Version 7.5.2".
CudaVersionInfo parseCudaVersionFile(llvm::StringRef V) {
V = V.trim();
if (!V.startswith("CUDA Version "))
return {V.str(), CudaVersion::UNKNOWN};
V = V.substr(strlen("CUDA Version "));
SmallVector<StringRef,4> VersionParts;
V.split(VersionParts, '.');
return {"version.txt: " + V.str() + ".",
VersionParts.size() < 2
? CudaVersion::UNKNOWN
: CudaStringToVersion(
join_items(".", VersionParts[0], VersionParts[1]))};
}
CudaVersion getCudaVersion(uint32_t raw_version) {
if (raw_version < 7050)
@ -83,10 +65,10 @@ CudaVersion getCudaVersion(uint32_t raw_version) {
return CudaVersion::CUDA_113;
if (raw_version < 11050)
return CudaVersion::CUDA_114;
return CudaVersion::LATEST;
return CudaVersion::NEW;
}
CudaVersionInfo parseCudaHFile(llvm::StringRef Input) {
CudaVersion parseCudaHFile(llvm::StringRef Input) {
// Helper lambda which skips the words if the line starts with them or returns
// None otherwise.
auto StartsWithWords =
@ -106,21 +88,27 @@ CudaVersionInfo parseCudaHFile(llvm::StringRef Input) {
StartsWithWords(Input.ltrim(), {"#", "define", "CUDA_VERSION"})) {
uint32_t RawVersion;
Line->consumeInteger(10, RawVersion);
return {"cuda.h: CUDA_VERSION=" + Twine(RawVersion).str() + ".",
getCudaVersion(RawVersion)};
return getCudaVersion(RawVersion);
}
// Find next non-empty line.
Input = Input.drop_front(Input.find_first_of("\n\r")).ltrim();
}
return {"cuda.h: CUDA_VERSION not found.", CudaVersion::UNKNOWN};
return CudaVersion::UNKNOWN;
}
} // namespace
void CudaInstallationDetector::WarnIfUnsupportedVersion() {
if (DetectedVersionIsNotSupported)
D.Diag(diag::warn_drv_unknown_cuda_version)
<< DetectedVersion
<< CudaVersionToString(CudaVersion::LATEST_SUPPORTED);
if (Version > CudaVersion::PARTIALLY_SUPPORTED) {
std::string VersionString = CudaVersionToString(Version);
if (!VersionString.empty())
VersionString.insert(0, " ");
D.Diag(diag::warn_drv_new_cuda_version)
<< VersionString
<< (CudaVersion::PARTIALLY_SUPPORTED != CudaVersion::FULLY_SUPPORTED)
<< CudaVersionToString(CudaVersion::PARTIALLY_SUPPORTED);
} else if (Version > CudaVersion::FULLY_SUPPORTED)
D.Diag(diag::warn_drv_partially_supported_cuda_version)
<< CudaVersionToString(Version);
}
CudaInstallationDetector::CudaInstallationDetector(
@ -212,30 +200,17 @@ CudaInstallationDetector::CudaInstallationDetector(
else
continue;
CudaVersionInfo VersionInfo = {"", CudaVersion::UNKNOWN};
if (auto VersionFile = FS.getBufferForFile(InstallPath + "/version.txt"))
VersionInfo = parseCudaVersionFile((*VersionFile)->getBuffer());
// If version file didn't give us the version, try to find it in cuda.h
if (VersionInfo.Version == CudaVersion::UNKNOWN)
if (auto CudaHFile = FS.getBufferForFile(InstallPath + "/include/cuda.h"))
VersionInfo = parseCudaHFile((*CudaHFile)->getBuffer());
// As the last resort, make an educated guess between CUDA-7.0, (which had
// no version.txt file and had old-style libdevice bitcode ) and an unknown
// recent CUDA version (no version.txt, new style bitcode).
if (VersionInfo.Version == CudaVersion::UNKNOWN) {
VersionInfo.Version = (FS.exists(LibDevicePath + "/libdevice.10.bc"))
? Version = CudaVersion::LATEST
: Version = CudaVersion::CUDA_70;
VersionInfo.DetectedVersion = "no version found in version.txt or cuda.h";
Version = CudaVersion::UNKNOWN;
if (auto CudaHFile = FS.getBufferForFile(InstallPath + "/include/cuda.h"))
Version = parseCudaHFile((*CudaHFile)->getBuffer());
// As the last resort, make an educated guess between CUDA-7.0, which had
// old-style libdevice bitcode, and an unknown recent CUDA version.
if (Version == CudaVersion::UNKNOWN) {
Version = FS.exists(LibDevicePath + "/libdevice.10.bc")
? CudaVersion::NEW
: CudaVersion::CUDA_70;
}
Version = VersionInfo.Version;
DetectedVersion = VersionInfo.DetectedVersion;
// TODO(tra): remove the warning once we have all features of 10.2
// and 11.0 implemented.
DetectedVersionIsNotSupported = Version > CudaVersion::LATEST_SUPPORTED;
if (Version >= CudaVersion::CUDA_90) {
// CUDA-9+ uses single libdevice file for all GPU variants.
std::string FilePath = LibDevicePath + "/libdevice.10.bc";

View File

@ -30,8 +30,6 @@ private:
const Driver &D;
bool IsValid = false;
CudaVersion Version = CudaVersion::UNKNOWN;
std::string DetectedVersion;
bool DetectedVersionIsNotSupported = false;
std::string InstallPath;
std::string BinPath;
std::string LibPath;
@ -62,7 +60,10 @@ public:
void print(raw_ostream &OS) const;
/// Get the detected Cuda install's version.
CudaVersion version() const { return Version; }
CudaVersion version() const {
return Version == CudaVersion::NEW ? CudaVersion::PARTIALLY_SUPPORTED
: Version;
}
/// Get the detected Cuda installation path.
StringRef getInstallPath() const { return InstallPath; }
/// Get the detected path to Cuda's bin directory.

View File

@ -0,0 +1,7 @@
//
// Placeholder file for testing CUDA version detection
//
#define CUDA_VERSION 99010
//

View File

@ -1 +0,0 @@
CUDA Version 999.999.999

View File

@ -0,0 +1,7 @@
//
// Placeholder file for testing CUDA version detection
//
#define CUDA_VERSION 8000
//

View File

@ -1 +0,0 @@
CUDA Version 8.0.42

View File

@ -0,0 +1,7 @@
//
// Placeholder file for testing CUDA version detection
//
#define CUDA_VERSION 9000
//

View File

@ -8,15 +8,12 @@
// RUN: FileCheck %s --check-prefix=OK
// RUN: %clang --target=x86_64-linux -v -### --cuda-gpu-arch=sm_60 --cuda-path=%S/Inputs/CUDA_80/usr/local/cuda 2>&1 %s | \
// RUN: FileCheck %s --check-prefix=OK
// Test version guess when no version.txt or cuda.h are found
// Test version guess when cuda.h has not been found
// RUN: %clang --target=x86_64-linux -v -### --cuda-gpu-arch=sm_60 --cuda-path=%S/Inputs/CUDA-unknown/usr/local/cuda 2>&1 %s | \
// RUN: FileCheck %s --check-prefix=UNKNOWN_VERSION
// Unknown version with version.txt present
// RUN: %clang --target=x86_64-linux -v -### --cuda-gpu-arch=sm_60 --cuda-path=%S/Inputs/CUDA_102/usr/local/cuda 2>&1 %s | \
// RUN: FileCheck %s --check-prefix=UNKNOWN_VERSION_V
// Unknown version with no version.txt but with version info present in cuda.h
// RUN: %clang --target=x86_64-linux -v -### --cuda-gpu-arch=sm_60 --cuda-path=%S/Inputs/CUDA_111/usr/local/cuda 2>&1 %s | \
// RUN: FileCheck %s --check-prefix=UNKNOWN_VERSION_H
// Unknown version info present in cuda.h
// RUN: %clang --target=x86_64-linux -v -### --cuda-gpu-arch=sm_60 --cuda-path=%S/Inputs/CUDA-new/usr/local/cuda 2>&1 %s | \
// RUN: FileCheck %s --check-prefix=UNKNOWN_VERSION
// Make sure that we don't warn about CUDA version during C++ compilation.
// RUN: %clang --target=x86_64-linux -v -### -x c++ --cuda-gpu-arch=sm_60 \
// RUN: --cuda-path=%S/Inputs/CUDA-unknown/usr/local/cuda 2>&1 %s | \
@ -66,13 +63,14 @@
// OK_SM35-NOT: error: GPU arch sm_35
// We should only get one error per architecture.
// ERR_SM20: error: GPU arch sm_20 {{.*}}
// ERR_SM20-NOT: error: GPU arch sm_20
// ERR_SM60: error: GPU arch sm_60 {{.*}}
// ERR_SM60-NOT: error: GPU arch sm_60
// ERR_SM61: error: GPU arch sm_61 {{.*}}
// ERR_SM61-NOT: error: GPU arch sm_61
// UNKNOWN_VERSION_V: unknown CUDA version: version.txt:{{.*}}; assuming the latest supported version
// UNKNOWN_VERSION_H: unknown CUDA version: cuda.h: CUDA_VERSION={{.*}}; assuming the latest supported version
// UNKNOWN_VERSION: unknown CUDA version: no version found in version.txt or cuda.h; assuming the latest supported version
// UNKNOWN_VERSION: CUDA version is newer than the latest{{.*}} supported version
// UNKNOWN_VERSION_CXX-NOT: unknown CUDA version