1616#include " clang/Driver/Driver.h"
1717#include " clang/Driver/DriverDiagnostic.h"
1818#include " clang/Driver/Options.h"
19+ #include " llvm/ADT/Optional.h"
1920#include " llvm/Option/ArgList.h"
2021#include " llvm/Support/FileSystem.h"
2122#include " llvm/Support/Host.h"
@@ -32,29 +33,80 @@ using namespace clang::driver::tools;
3233using namespace clang ;
3334using namespace llvm ::opt;
3435
36+ namespace {
37+ struct CudaVersionInfo {
38+ std::string DetectedVersion;
39+ CudaVersion Version;
40+ };
3541// Parses the contents of version.txt in an CUDA installation. It should
3642// contain one line of the from e.g. "CUDA Version 7.5.2".
37- void CudaInstallationDetector::ParseCudaVersionFile (llvm::StringRef V) {
38- Version = CudaVersion::UNKNOWN ;
43+ CudaVersionInfo parseCudaVersionFile (llvm::StringRef V) {
44+ V = V. trim () ;
3945 if (!V.startswith (" CUDA Version " ))
40- return ;
46+ return {V. str (), CudaVersion::UNKNOWN} ;
4147 V = V.substr (strlen (" CUDA Version " ));
4248 SmallVector<StringRef,4 > VersionParts;
4349 V.split (VersionParts, ' .' );
44- if (VersionParts.size () < 2 )
45- return ;
46- DetectedVersion = join_items (" ." , VersionParts[0 ], VersionParts[1 ]);
47- Version = CudaStringToVersion (DetectedVersion);
48- if (Version != CudaVersion::UNKNOWN) {
49- // TODO(tra): remove the warning once we have all features of 10.2 and 11.0
50- // implemented.
51- DetectedVersionIsNotSupported = Version > CudaVersion::LATEST_SUPPORTED;
52- return ;
53- }
50+ return {" version.txt: " + V.str () + " ." ,
51+ VersionParts.size () < 2
52+ ? CudaVersion::UNKNOWN
53+ : CudaStringToVersion (
54+ join_items (" ." , VersionParts[0 ], VersionParts[1 ]))};
55+ }
56+
57+ CudaVersion getCudaVersion (uint32_t raw_version) {
58+ if (raw_version < 7050 )
59+ return CudaVersion::CUDA_70;
60+ if (raw_version < 8000 )
61+ return CudaVersion::CUDA_75;
62+ if (raw_version < 9000 )
63+ return CudaVersion::CUDA_80;
64+ if (raw_version < 9010 )
65+ return CudaVersion::CUDA_90;
66+ if (raw_version < 9020 )
67+ return CudaVersion::CUDA_91;
68+ if (raw_version < 10000 )
69+ return CudaVersion::CUDA_92;
70+ if (raw_version < 10010 )
71+ return CudaVersion::CUDA_100;
72+ if (raw_version < 10020 )
73+ return CudaVersion::CUDA_101;
74+ if (raw_version < 11000 )
75+ return CudaVersion::CUDA_102;
76+ if (raw_version < 11010 )
77+ return CudaVersion::CUDA_110;
78+ return CudaVersion::LATEST;
79+ }
5480
55- Version = CudaVersion::LATEST_SUPPORTED;
56- DetectedVersionIsNotSupported = true ;
81+ CudaVersionInfo parseCudaHFile (llvm::StringRef Input) {
82+ // Helper lambda which skips the words if the line starts with them or returns
83+ // None otherwise.
84+ auto StartsWithWords =
85+ [](llvm::StringRef Line,
86+ const SmallVector<StringRef, 3 > words) -> llvm::Optional<StringRef> {
87+ for (StringRef word : words) {
88+ if (!Line.consume_front (word))
89+ return {};
90+ Line = Line.ltrim ();
91+ }
92+ return Line;
93+ };
94+
95+ Input = Input.ltrim ();
96+ while (!Input.empty ()) {
97+ if (auto Line =
98+ StartsWithWords (Input.ltrim (), {" #" , " define" , " CUDA_VERSION" })) {
99+ uint32_t RawVersion;
100+ Line->consumeInteger (10 , RawVersion);
101+ return {" cuda.h: CUDA_VERSION=" + Twine (RawVersion).str () + " ." ,
102+ getCudaVersion (RawVersion)};
103+ }
104+ // Find next non-empty line.
105+ Input = Input.drop_front (Input.find_first_of (" \n\r " )).ltrim ();
106+ }
107+ return {" cuda.h: CUDA_VERSION not found." , CudaVersion::UNKNOWN};
57108}
109+ } // namespace
58110
59111void CudaInstallationDetector::WarnIfUnsupportedVersion () {
60112 if (DetectedVersionIsNotSupported)
@@ -152,21 +204,31 @@ CudaInstallationDetector::CudaInstallationDetector(
152204 else
153205 continue ;
154206
155- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> VersionFile =
156- FS.getBufferForFile (InstallPath + " /version.txt" );
157- if (!VersionFile) {
158- // CUDA 7.0 and CUDA 11.1+ do not have version.txt file.
159- // Use libdevice file to distinguish 7.0 from the new versions.
160- if (FS.exists (LibDevicePath + " /libdevice.10.bc" )) {
161- Version = CudaVersion::LATEST;
162- DetectedVersionIsNotSupported = Version > CudaVersion::LATEST_SUPPORTED;
163- } else {
164- Version = CudaVersion::CUDA_70;
165- }
166- } else {
167- ParseCudaVersionFile ((*VersionFile)->getBuffer ());
207+ CudaVersionInfo VersionInfo = {" " , CudaVersion::UNKNOWN};
208+ if (auto VersionFile = FS.getBufferForFile (InstallPath + " /version.txt" ))
209+ VersionInfo = parseCudaVersionFile ((*VersionFile)->getBuffer ());
210+ // If version file didn't give us the version, try to find it in cuda.h
211+ if (VersionInfo.Version == CudaVersion::UNKNOWN)
212+ if (auto CudaHFile = FS.getBufferForFile (InstallPath + " /include/cuda.h" ))
213+ VersionInfo = parseCudaHFile ((*CudaHFile)->getBuffer ());
214+ // As the last resort, make an educated guess between CUDA-7.0, (which had
215+ // no version.txt file and had old-style libdevice bitcode ) and an unknown
216+ // recent CUDA version (no version.txt, new style bitcode).
217+ if (VersionInfo.Version == CudaVersion::UNKNOWN) {
218+ VersionInfo.Version = (FS.exists (LibDevicePath + " /libdevice.10.bc" ))
219+ ? Version = CudaVersion::LATEST
220+ : Version = CudaVersion::CUDA_70;
221+ VersionInfo.DetectedVersion =
222+ " No version found in version.txt or cuda.h." ;
168223 }
169224
225+ Version = VersionInfo.Version ;
226+ DetectedVersion = VersionInfo.DetectedVersion ;
227+
228+ // TODO(tra): remove the warning once we have all features of 10.2
229+ // and 11.0 implemented.
230+ DetectedVersionIsNotSupported = Version > CudaVersion::LATEST_SUPPORTED;
231+
170232 if (Version >= CudaVersion::CUDA_90) {
171233 // CUDA-9+ uses single libdevice file for all GPU variants.
172234 std::string FilePath = LibDevicePath + " /libdevice.10.bc" ;
0 commit comments