xref: /freebsd-src/contrib/llvm-project/clang/lib/Driver/ToolChains/AMDGPUOpenMP.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
1*fe6060f1SDimitry Andric //===- AMDGPUOpenMP.cpp - AMDGPUOpenMP ToolChain Implementation -*- C++ -*-===//
2*fe6060f1SDimitry Andric //
3*fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*fe6060f1SDimitry Andric //
7*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8*fe6060f1SDimitry Andric 
9*fe6060f1SDimitry Andric #include "AMDGPUOpenMP.h"
10*fe6060f1SDimitry Andric #include "AMDGPU.h"
11*fe6060f1SDimitry Andric #include "CommonArgs.h"
12*fe6060f1SDimitry Andric #include "clang/Basic/DiagnosticDriver.h"
13*fe6060f1SDimitry Andric #include "clang/Driver/Compilation.h"
14*fe6060f1SDimitry Andric #include "clang/Driver/Driver.h"
15*fe6060f1SDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
16*fe6060f1SDimitry Andric #include "clang/Driver/InputInfo.h"
17*fe6060f1SDimitry Andric #include "clang/Driver/Options.h"
18*fe6060f1SDimitry Andric #include "llvm/Support/FileSystem.h"
19*fe6060f1SDimitry Andric #include "llvm/Support/FormatAdapters.h"
20*fe6060f1SDimitry Andric #include "llvm/Support/FormatVariadic.h"
21*fe6060f1SDimitry Andric #include "llvm/Support/Path.h"
22*fe6060f1SDimitry Andric 
23*fe6060f1SDimitry Andric using namespace clang::driver;
24*fe6060f1SDimitry Andric using namespace clang::driver::toolchains;
25*fe6060f1SDimitry Andric using namespace clang::driver::tools;
26*fe6060f1SDimitry Andric using namespace clang;
27*fe6060f1SDimitry Andric using namespace llvm::opt;
28*fe6060f1SDimitry Andric 
29*fe6060f1SDimitry Andric namespace {
30*fe6060f1SDimitry Andric 
31*fe6060f1SDimitry Andric static const char *getOutputFileName(Compilation &C, StringRef Base,
32*fe6060f1SDimitry Andric                                      const char *Postfix,
33*fe6060f1SDimitry Andric                                      const char *Extension) {
34*fe6060f1SDimitry Andric   const char *OutputFileName;
35*fe6060f1SDimitry Andric   if (C.getDriver().isSaveTempsEnabled()) {
36*fe6060f1SDimitry Andric     OutputFileName =
37*fe6060f1SDimitry Andric         C.getArgs().MakeArgString(Base.str() + Postfix + "." + Extension);
38*fe6060f1SDimitry Andric   } else {
39*fe6060f1SDimitry Andric     std::string TmpName =
40*fe6060f1SDimitry Andric         C.getDriver().GetTemporaryPath(Base.str() + Postfix, Extension);
41*fe6060f1SDimitry Andric     OutputFileName = C.addTempFile(C.getArgs().MakeArgString(TmpName));
42*fe6060f1SDimitry Andric   }
43*fe6060f1SDimitry Andric   return OutputFileName;
44*fe6060f1SDimitry Andric }
45*fe6060f1SDimitry Andric 
46*fe6060f1SDimitry Andric static void addLLCOptArg(const llvm::opt::ArgList &Args,
47*fe6060f1SDimitry Andric                          llvm::opt::ArgStringList &CmdArgs) {
48*fe6060f1SDimitry Andric   if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
49*fe6060f1SDimitry Andric     StringRef OOpt = "0";
50*fe6060f1SDimitry Andric     if (A->getOption().matches(options::OPT_O4) ||
51*fe6060f1SDimitry Andric         A->getOption().matches(options::OPT_Ofast))
52*fe6060f1SDimitry Andric       OOpt = "3";
53*fe6060f1SDimitry Andric     else if (A->getOption().matches(options::OPT_O0))
54*fe6060f1SDimitry Andric       OOpt = "0";
55*fe6060f1SDimitry Andric     else if (A->getOption().matches(options::OPT_O)) {
56*fe6060f1SDimitry Andric       // Clang and opt support -Os/-Oz; llc only supports -O0, -O1, -O2 and -O3
57*fe6060f1SDimitry Andric       // so we map -Os/-Oz to -O2.
58*fe6060f1SDimitry Andric       // Only clang supports -Og, and maps it to -O1.
59*fe6060f1SDimitry Andric       // We map anything else to -O2.
60*fe6060f1SDimitry Andric       OOpt = llvm::StringSwitch<const char *>(A->getValue())
61*fe6060f1SDimitry Andric                  .Case("1", "1")
62*fe6060f1SDimitry Andric                  .Case("2", "2")
63*fe6060f1SDimitry Andric                  .Case("3", "3")
64*fe6060f1SDimitry Andric                  .Case("s", "2")
65*fe6060f1SDimitry Andric                  .Case("z", "2")
66*fe6060f1SDimitry Andric                  .Case("g", "1")
67*fe6060f1SDimitry Andric                  .Default("0");
68*fe6060f1SDimitry Andric     }
69*fe6060f1SDimitry Andric     CmdArgs.push_back(Args.MakeArgString("-O" + OOpt));
70*fe6060f1SDimitry Andric   }
71*fe6060f1SDimitry Andric }
72*fe6060f1SDimitry Andric 
73*fe6060f1SDimitry Andric static bool checkSystemForAMDGPU(const ArgList &Args, const AMDGPUToolChain &TC,
74*fe6060f1SDimitry Andric                                  std::string &GPUArch) {
75*fe6060f1SDimitry Andric   if (auto Err = TC.getSystemGPUArch(Args, GPUArch)) {
76*fe6060f1SDimitry Andric     std::string ErrMsg =
77*fe6060f1SDimitry Andric         llvm::formatv("{0}", llvm::fmt_consume(std::move(Err)));
78*fe6060f1SDimitry Andric     TC.getDriver().Diag(diag::err_drv_undetermined_amdgpu_arch) << ErrMsg;
79*fe6060f1SDimitry Andric     return false;
80*fe6060f1SDimitry Andric   }
81*fe6060f1SDimitry Andric 
82*fe6060f1SDimitry Andric   return true;
83*fe6060f1SDimitry Andric }
84*fe6060f1SDimitry Andric } // namespace
85*fe6060f1SDimitry Andric 
86*fe6060f1SDimitry Andric const char *AMDGCN::OpenMPLinker::constructLLVMLinkCommand(
87*fe6060f1SDimitry Andric     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
88*fe6060f1SDimitry Andric     const ArgList &Args, StringRef SubArchName,
89*fe6060f1SDimitry Andric     StringRef OutputFilePrefix) const {
90*fe6060f1SDimitry Andric   ArgStringList CmdArgs;
91*fe6060f1SDimitry Andric 
92*fe6060f1SDimitry Andric   for (const auto &II : Inputs)
93*fe6060f1SDimitry Andric     if (II.isFilename())
94*fe6060f1SDimitry Andric       CmdArgs.push_back(II.getFilename());
95*fe6060f1SDimitry Andric   // Add an intermediate output file.
96*fe6060f1SDimitry Andric   CmdArgs.push_back("-o");
97*fe6060f1SDimitry Andric   const char *OutputFileName =
98*fe6060f1SDimitry Andric       getOutputFileName(C, OutputFilePrefix, "-linked", "bc");
99*fe6060f1SDimitry Andric   CmdArgs.push_back(OutputFileName);
100*fe6060f1SDimitry Andric   const char *Exec =
101*fe6060f1SDimitry Andric       Args.MakeArgString(getToolChain().GetProgramPath("llvm-link"));
102*fe6060f1SDimitry Andric   C.addCommand(std::make_unique<Command>(
103*fe6060f1SDimitry Andric       JA, *this, ResponseFileSupport::AtFileCurCP(), Exec, CmdArgs, Inputs,
104*fe6060f1SDimitry Andric       InputInfo(&JA, Args.MakeArgString(OutputFileName))));
105*fe6060f1SDimitry Andric   return OutputFileName;
106*fe6060f1SDimitry Andric }
107*fe6060f1SDimitry Andric 
108*fe6060f1SDimitry Andric const char *AMDGCN::OpenMPLinker::constructLlcCommand(
109*fe6060f1SDimitry Andric     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
110*fe6060f1SDimitry Andric     const llvm::opt::ArgList &Args, llvm::StringRef SubArchName,
111*fe6060f1SDimitry Andric     llvm::StringRef OutputFilePrefix, const char *InputFileName,
112*fe6060f1SDimitry Andric     bool OutputIsAsm) const {
113*fe6060f1SDimitry Andric   // Construct llc command.
114*fe6060f1SDimitry Andric   ArgStringList LlcArgs;
115*fe6060f1SDimitry Andric   // The input to llc is the output from opt.
116*fe6060f1SDimitry Andric   LlcArgs.push_back(InputFileName);
117*fe6060f1SDimitry Andric   // Pass optimization arg to llc.
118*fe6060f1SDimitry Andric   addLLCOptArg(Args, LlcArgs);
119*fe6060f1SDimitry Andric   LlcArgs.push_back("-mtriple=amdgcn-amd-amdhsa");
120*fe6060f1SDimitry Andric   LlcArgs.push_back(Args.MakeArgString("-mcpu=" + SubArchName));
121*fe6060f1SDimitry Andric   LlcArgs.push_back(
122*fe6060f1SDimitry Andric       Args.MakeArgString(Twine("-filetype=") + (OutputIsAsm ? "asm" : "obj")));
123*fe6060f1SDimitry Andric 
124*fe6060f1SDimitry Andric   for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
125*fe6060f1SDimitry Andric     LlcArgs.push_back(A->getValue(0));
126*fe6060f1SDimitry Andric   }
127*fe6060f1SDimitry Andric 
128*fe6060f1SDimitry Andric   // Add output filename
129*fe6060f1SDimitry Andric   LlcArgs.push_back("-o");
130*fe6060f1SDimitry Andric   const char *LlcOutputFile =
131*fe6060f1SDimitry Andric       getOutputFileName(C, OutputFilePrefix, "", OutputIsAsm ? "s" : "o");
132*fe6060f1SDimitry Andric   LlcArgs.push_back(LlcOutputFile);
133*fe6060f1SDimitry Andric   const char *Llc = Args.MakeArgString(getToolChain().GetProgramPath("llc"));
134*fe6060f1SDimitry Andric   C.addCommand(std::make_unique<Command>(
135*fe6060f1SDimitry Andric       JA, *this, ResponseFileSupport::AtFileCurCP(), Llc, LlcArgs, Inputs,
136*fe6060f1SDimitry Andric       InputInfo(&JA, Args.MakeArgString(LlcOutputFile))));
137*fe6060f1SDimitry Andric   return LlcOutputFile;
138*fe6060f1SDimitry Andric }
139*fe6060f1SDimitry Andric 
140*fe6060f1SDimitry Andric void AMDGCN::OpenMPLinker::constructLldCommand(
141*fe6060f1SDimitry Andric     Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
142*fe6060f1SDimitry Andric     const InputInfo &Output, const llvm::opt::ArgList &Args,
143*fe6060f1SDimitry Andric     const char *InputFileName) const {
144*fe6060f1SDimitry Andric   // Construct lld command.
145*fe6060f1SDimitry Andric   // The output from ld.lld is an HSA code object file.
146*fe6060f1SDimitry Andric   ArgStringList LldArgs{"-flavor",    "gnu", "--no-undefined",
147*fe6060f1SDimitry Andric                         "-shared",    "-o",  Output.getFilename(),
148*fe6060f1SDimitry Andric                         InputFileName};
149*fe6060f1SDimitry Andric 
150*fe6060f1SDimitry Andric   const char *Lld = Args.MakeArgString(getToolChain().GetProgramPath("lld"));
151*fe6060f1SDimitry Andric   C.addCommand(std::make_unique<Command>(
152*fe6060f1SDimitry Andric       JA, *this, ResponseFileSupport::AtFileCurCP(), Lld, LldArgs, Inputs,
153*fe6060f1SDimitry Andric       InputInfo(&JA, Args.MakeArgString(Output.getFilename()))));
154*fe6060f1SDimitry Andric }
155*fe6060f1SDimitry Andric 
156*fe6060f1SDimitry Andric // For amdgcn the inputs of the linker job are device bitcode and output is
157*fe6060f1SDimitry Andric // object file. It calls llvm-link, opt, llc, then lld steps.
158*fe6060f1SDimitry Andric void AMDGCN::OpenMPLinker::ConstructJob(Compilation &C, const JobAction &JA,
159*fe6060f1SDimitry Andric                                         const InputInfo &Output,
160*fe6060f1SDimitry Andric                                         const InputInfoList &Inputs,
161*fe6060f1SDimitry Andric                                         const ArgList &Args,
162*fe6060f1SDimitry Andric                                         const char *LinkingOutput) const {
163*fe6060f1SDimitry Andric   const ToolChain &TC = getToolChain();
164*fe6060f1SDimitry Andric   assert(getToolChain().getTriple().isAMDGCN() && "Unsupported target");
165*fe6060f1SDimitry Andric 
166*fe6060f1SDimitry Andric   const toolchains::AMDGPUOpenMPToolChain &AMDGPUOpenMPTC =
167*fe6060f1SDimitry Andric       static_cast<const toolchains::AMDGPUOpenMPToolChain &>(TC);
168*fe6060f1SDimitry Andric 
169*fe6060f1SDimitry Andric   std::string GPUArch = Args.getLastArgValue(options::OPT_march_EQ).str();
170*fe6060f1SDimitry Andric   if (GPUArch.empty()) {
171*fe6060f1SDimitry Andric     if (!checkSystemForAMDGPU(Args, AMDGPUOpenMPTC, GPUArch))
172*fe6060f1SDimitry Andric       return;
173*fe6060f1SDimitry Andric   }
174*fe6060f1SDimitry Andric 
175*fe6060f1SDimitry Andric   // Prefix for temporary file name.
176*fe6060f1SDimitry Andric   std::string Prefix;
177*fe6060f1SDimitry Andric   for (const auto &II : Inputs)
178*fe6060f1SDimitry Andric     if (II.isFilename())
179*fe6060f1SDimitry Andric       Prefix = llvm::sys::path::stem(II.getFilename()).str() + "-" + GPUArch;
180*fe6060f1SDimitry Andric   assert(Prefix.length() && "no linker inputs are files ");
181*fe6060f1SDimitry Andric 
182*fe6060f1SDimitry Andric   // Each command outputs different files.
183*fe6060f1SDimitry Andric   const char *LLVMLinkCommand =
184*fe6060f1SDimitry Andric       constructLLVMLinkCommand(C, JA, Inputs, Args, GPUArch, Prefix);
185*fe6060f1SDimitry Andric 
186*fe6060f1SDimitry Andric   // Produce readable assembly if save-temps is enabled.
187*fe6060f1SDimitry Andric   if (C.getDriver().isSaveTempsEnabled())
188*fe6060f1SDimitry Andric     constructLlcCommand(C, JA, Inputs, Args, GPUArch, Prefix, LLVMLinkCommand,
189*fe6060f1SDimitry Andric                         /*OutputIsAsm=*/true);
190*fe6060f1SDimitry Andric   const char *LlcCommand = constructLlcCommand(C, JA, Inputs, Args, GPUArch,
191*fe6060f1SDimitry Andric                                                Prefix, LLVMLinkCommand);
192*fe6060f1SDimitry Andric   constructLldCommand(C, JA, Inputs, Output, Args, LlcCommand);
193*fe6060f1SDimitry Andric }
194*fe6060f1SDimitry Andric 
195*fe6060f1SDimitry Andric AMDGPUOpenMPToolChain::AMDGPUOpenMPToolChain(const Driver &D,
196*fe6060f1SDimitry Andric                                              const llvm::Triple &Triple,
197*fe6060f1SDimitry Andric                                              const ToolChain &HostTC,
198*fe6060f1SDimitry Andric                                              const ArgList &Args)
199*fe6060f1SDimitry Andric     : ROCMToolChain(D, Triple, Args), HostTC(HostTC) {
200*fe6060f1SDimitry Andric   // Lookup binaries into the driver directory, this is used to
201*fe6060f1SDimitry Andric   // discover the clang-offload-bundler executable.
202*fe6060f1SDimitry Andric   getProgramPaths().push_back(getDriver().Dir);
203*fe6060f1SDimitry Andric }
204*fe6060f1SDimitry Andric 
205*fe6060f1SDimitry Andric void AMDGPUOpenMPToolChain::addClangTargetOptions(
206*fe6060f1SDimitry Andric     const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
207*fe6060f1SDimitry Andric     Action::OffloadKind DeviceOffloadingKind) const {
208*fe6060f1SDimitry Andric   HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind);
209*fe6060f1SDimitry Andric 
210*fe6060f1SDimitry Andric   std::string GPUArch = DriverArgs.getLastArgValue(options::OPT_march_EQ).str();
211*fe6060f1SDimitry Andric   if (GPUArch.empty()) {
212*fe6060f1SDimitry Andric     if (!checkSystemForAMDGPU(DriverArgs, *this, GPUArch))
213*fe6060f1SDimitry Andric       return;
214*fe6060f1SDimitry Andric   }
215*fe6060f1SDimitry Andric 
216*fe6060f1SDimitry Andric   assert(DeviceOffloadingKind == Action::OFK_OpenMP &&
217*fe6060f1SDimitry Andric          "Only OpenMP offloading kinds are supported.");
218*fe6060f1SDimitry Andric 
219*fe6060f1SDimitry Andric   CC1Args.push_back("-target-cpu");
220*fe6060f1SDimitry Andric   CC1Args.push_back(DriverArgs.MakeArgStringRef(GPUArch));
221*fe6060f1SDimitry Andric   CC1Args.push_back("-fcuda-is-device");
222*fe6060f1SDimitry Andric 
223*fe6060f1SDimitry Andric   if (DriverArgs.hasArg(options::OPT_nogpulib))
224*fe6060f1SDimitry Andric     return;
225*fe6060f1SDimitry Andric 
226*fe6060f1SDimitry Andric   std::string BitcodeSuffix;
227*fe6060f1SDimitry Andric   if (DriverArgs.hasFlag(options::OPT_fopenmp_target_new_runtime,
228*fe6060f1SDimitry Andric                          options::OPT_fno_openmp_target_new_runtime, false))
229*fe6060f1SDimitry Andric     BitcodeSuffix = "new-amdgcn-" + GPUArch;
230*fe6060f1SDimitry Andric   else
231*fe6060f1SDimitry Andric     BitcodeSuffix = "amdgcn-" + GPUArch;
232*fe6060f1SDimitry Andric 
233*fe6060f1SDimitry Andric   addOpenMPDeviceRTL(getDriver(), DriverArgs, CC1Args, BitcodeSuffix,
234*fe6060f1SDimitry Andric                      getTriple());
235*fe6060f1SDimitry Andric }
236*fe6060f1SDimitry Andric 
237*fe6060f1SDimitry Andric llvm::opt::DerivedArgList *AMDGPUOpenMPToolChain::TranslateArgs(
238*fe6060f1SDimitry Andric     const llvm::opt::DerivedArgList &Args, StringRef BoundArch,
239*fe6060f1SDimitry Andric     Action::OffloadKind DeviceOffloadKind) const {
240*fe6060f1SDimitry Andric   DerivedArgList *DAL =
241*fe6060f1SDimitry Andric       HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind);
242*fe6060f1SDimitry Andric   if (!DAL)
243*fe6060f1SDimitry Andric     DAL = new DerivedArgList(Args.getBaseArgs());
244*fe6060f1SDimitry Andric 
245*fe6060f1SDimitry Andric   const OptTable &Opts = getDriver().getOpts();
246*fe6060f1SDimitry Andric 
247*fe6060f1SDimitry Andric   if (DeviceOffloadKind != Action::OFK_OpenMP) {
248*fe6060f1SDimitry Andric     for (Arg *A : Args) {
249*fe6060f1SDimitry Andric       DAL->append(A);
250*fe6060f1SDimitry Andric     }
251*fe6060f1SDimitry Andric   }
252*fe6060f1SDimitry Andric 
253*fe6060f1SDimitry Andric   if (!BoundArch.empty()) {
254*fe6060f1SDimitry Andric     DAL->eraseArg(options::OPT_march_EQ);
255*fe6060f1SDimitry Andric     DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ),
256*fe6060f1SDimitry Andric                       BoundArch);
257*fe6060f1SDimitry Andric   }
258*fe6060f1SDimitry Andric 
259*fe6060f1SDimitry Andric   return DAL;
260*fe6060f1SDimitry Andric }
261*fe6060f1SDimitry Andric 
262*fe6060f1SDimitry Andric Tool *AMDGPUOpenMPToolChain::buildLinker() const {
263*fe6060f1SDimitry Andric   assert(getTriple().isAMDGCN());
264*fe6060f1SDimitry Andric   return new tools::AMDGCN::OpenMPLinker(*this);
265*fe6060f1SDimitry Andric }
266*fe6060f1SDimitry Andric 
267*fe6060f1SDimitry Andric void AMDGPUOpenMPToolChain::addClangWarningOptions(
268*fe6060f1SDimitry Andric     ArgStringList &CC1Args) const {
269*fe6060f1SDimitry Andric   HostTC.addClangWarningOptions(CC1Args);
270*fe6060f1SDimitry Andric }
271*fe6060f1SDimitry Andric 
272*fe6060f1SDimitry Andric ToolChain::CXXStdlibType
273*fe6060f1SDimitry Andric AMDGPUOpenMPToolChain::GetCXXStdlibType(const ArgList &Args) const {
274*fe6060f1SDimitry Andric   return HostTC.GetCXXStdlibType(Args);
275*fe6060f1SDimitry Andric }
276*fe6060f1SDimitry Andric 
277*fe6060f1SDimitry Andric void AMDGPUOpenMPToolChain::AddClangSystemIncludeArgs(
278*fe6060f1SDimitry Andric     const ArgList &DriverArgs, ArgStringList &CC1Args) const {
279*fe6060f1SDimitry Andric   HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args);
280*fe6060f1SDimitry Andric }
281*fe6060f1SDimitry Andric 
282*fe6060f1SDimitry Andric void AMDGPUOpenMPToolChain::AddIAMCUIncludeArgs(const ArgList &Args,
283*fe6060f1SDimitry Andric                                                 ArgStringList &CC1Args) const {
284*fe6060f1SDimitry Andric   HostTC.AddIAMCUIncludeArgs(Args, CC1Args);
285*fe6060f1SDimitry Andric }
286*fe6060f1SDimitry Andric 
287*fe6060f1SDimitry Andric SanitizerMask AMDGPUOpenMPToolChain::getSupportedSanitizers() const {
288*fe6060f1SDimitry Andric   // The AMDGPUOpenMPToolChain only supports sanitizers in the sense that it
289*fe6060f1SDimitry Andric   // allows sanitizer arguments on the command line if they are supported by the
290*fe6060f1SDimitry Andric   // host toolchain. The AMDGPUOpenMPToolChain will actually ignore any command
291*fe6060f1SDimitry Andric   // line arguments for any of these "supported" sanitizers. That means that no
292*fe6060f1SDimitry Andric   // sanitization of device code is actually supported at this time.
293*fe6060f1SDimitry Andric   //
294*fe6060f1SDimitry Andric   // This behavior is necessary because the host and device toolchains
295*fe6060f1SDimitry Andric   // invocations often share the command line, so the device toolchain must
296*fe6060f1SDimitry Andric   // tolerate flags meant only for the host toolchain.
297*fe6060f1SDimitry Andric   return HostTC.getSupportedSanitizers();
298*fe6060f1SDimitry Andric }
299*fe6060f1SDimitry Andric 
300*fe6060f1SDimitry Andric VersionTuple
301*fe6060f1SDimitry Andric AMDGPUOpenMPToolChain::computeMSVCVersion(const Driver *D,
302*fe6060f1SDimitry Andric                                           const ArgList &Args) const {
303*fe6060f1SDimitry Andric   return HostTC.computeMSVCVersion(D, Args);
304*fe6060f1SDimitry Andric }
305