1 | //===-- MSVC.cpp - MSVC ToolChain Implementations -------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "MSVC.h" |
10 | #include "CommonArgs.h" |
11 | #include "Darwin.h" |
12 | #include "clang/Basic/CharInfo.h" |
13 | #include "clang/Basic/Version.h" |
14 | #include "clang/Config/config.h" |
15 | #include "clang/Driver/Compilation.h" |
16 | #include "clang/Driver/Driver.h" |
17 | #include "clang/Driver/DriverDiagnostic.h" |
18 | #include "clang/Driver/Options.h" |
19 | #include "clang/Driver/SanitizerArgs.h" |
20 | #include "llvm/ADT/StringExtras.h" |
21 | #include "llvm/Option/Arg.h" |
22 | #include "llvm/Option/ArgList.h" |
23 | #include "llvm/Support/ConvertUTF.h" |
24 | #include "llvm/Support/ErrorHandling.h" |
25 | #include "llvm/Support/FileSystem.h" |
26 | #include "llvm/Support/MemoryBuffer.h" |
27 | #include "llvm/Support/Path.h" |
28 | #include "llvm/Support/Process.h" |
29 | #include "llvm/Support/VirtualFileSystem.h" |
30 | #include "llvm/TargetParser/Host.h" |
31 | #include <cstdio> |
32 | |
33 | #ifdef _WIN32 |
34 | #define WIN32_LEAN_AND_MEAN |
35 | #define NOGDI |
36 | #ifndef NOMINMAX |
37 | #define NOMINMAX |
38 | #endif |
39 | #include <windows.h> |
40 | #endif |
41 | |
42 | using namespace clang::driver; |
43 | using namespace clang::driver::toolchains; |
44 | using namespace clang::driver::tools; |
45 | using namespace clang; |
46 | using namespace llvm::opt; |
47 | |
48 | static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) { |
49 | auto Status = VFS.status(Path); |
50 | if (!Status) |
51 | return false; |
52 | return (Status->getPermissions() & llvm::sys::fs::perms::all_exe) != 0; |
53 | } |
54 | |
55 | // Try to find Exe from a Visual Studio distribution. This first tries to find |
56 | // an installed copy of Visual Studio and, failing that, looks in the PATH, |
57 | // making sure that whatever executable that's found is not a same-named exe |
58 | // from clang itself to prevent clang from falling back to itself. |
59 | static std::string FindVisualStudioExecutable(const ToolChain &TC, |
60 | const char *Exe) { |
61 | const auto &MSVC = static_cast<const toolchains::MSVCToolChain &>(TC); |
62 | SmallString<128> FilePath( |
63 | MSVC.getSubDirectoryPath(Type: llvm::SubDirectoryType::Bin)); |
64 | llvm::sys::path::append(path&: FilePath, a: Exe); |
65 | return std::string(canExecute(VFS&: TC.getVFS(), Path: FilePath) ? FilePath.str() : Exe); |
66 | } |
67 | |
68 | void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, |
69 | const InputInfo &Output, |
70 | const InputInfoList &Inputs, |
71 | const ArgList &Args, |
72 | const char *LinkingOutput) const { |
73 | ArgStringList CmdArgs; |
74 | |
75 | auto &TC = static_cast<const toolchains::MSVCToolChain &>(getToolChain()); |
76 | |
77 | assert((Output.isFilename() || Output.isNothing()) && "invalid output" ); |
78 | if (Output.isFilename()) |
79 | CmdArgs.push_back( |
80 | Elt: Args.MakeArgString(Str: std::string("-out:" ) + Output.getFilename())); |
81 | |
82 | if (Args.hasArg(options::OPT_marm64x)) |
83 | CmdArgs.push_back(Elt: "-machine:arm64x" ); |
84 | else if (TC.getTriple().isWindowsArm64EC()) |
85 | CmdArgs.push_back(Elt: "-machine:arm64ec" ); |
86 | |
87 | if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) && |
88 | !C.getDriver().IsCLMode() && !C.getDriver().IsFlangMode()) { |
89 | CmdArgs.push_back(Elt: "-defaultlib:libcmt" ); |
90 | CmdArgs.push_back(Elt: "-defaultlib:oldnames" ); |
91 | } |
92 | |
93 | // If the VC environment hasn't been configured (perhaps because the user |
94 | // did not run vcvarsall), try to build a consistent link environment. If |
95 | // the environment variable is set however, assume the user knows what |
96 | // they're doing. If the user passes /vctoolsdir or /winsdkdir, trust that |
97 | // over env vars. |
98 | if (const Arg *A = Args.getLastArg(options::OPT__SLASH_diasdkdir, |
99 | options::OPT__SLASH_winsysroot)) { |
100 | // cl.exe doesn't find the DIA SDK automatically, so this too requires |
101 | // explicit flags and doesn't automatically look in "DIA SDK" relative |
102 | // to the path we found for VCToolChainPath. |
103 | llvm::SmallString<128> DIAPath(A->getValue()); |
104 | if (A->getOption().getID() == options::OPT__SLASH_winsysroot) |
105 | llvm::sys::path::append(path&: DIAPath, a: "DIA SDK" ); |
106 | |
107 | // The DIA SDK always uses the legacy vc arch, even in new MSVC versions. |
108 | llvm::sys::path::append(path&: DIAPath, a: "lib" , |
109 | b: llvm::archToLegacyVCArch(Arch: TC.getArch())); |
110 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: Twine("-libpath:" ) + DIAPath)); |
111 | } |
112 | if (!llvm::sys::Process::GetEnv(name: "LIB" ) || |
113 | Args.getLastArg(options::OPT__SLASH_vctoolsdir, |
114 | options::OPT__SLASH_winsysroot)) { |
115 | CmdArgs.push_back(Elt: Args.MakeArgString( |
116 | Str: Twine("-libpath:" ) + |
117 | TC.getSubDirectoryPath(Type: llvm::SubDirectoryType::Lib))); |
118 | CmdArgs.push_back(Elt: Args.MakeArgString( |
119 | Str: Twine("-libpath:" ) + |
120 | TC.getSubDirectoryPath(Type: llvm::SubDirectoryType::Lib, SubdirParent: "atlmfc" ))); |
121 | } |
122 | if (!llvm::sys::Process::GetEnv(name: "LIB" ) || |
123 | Args.getLastArg(options::OPT__SLASH_winsdkdir, |
124 | options::OPT__SLASH_winsysroot)) { |
125 | if (TC.useUniversalCRT()) { |
126 | std::string UniversalCRTLibPath; |
127 | if (TC.getUniversalCRTLibraryPath(Args, path&: UniversalCRTLibPath)) |
128 | CmdArgs.push_back( |
129 | Elt: Args.MakeArgString(Str: Twine("-libpath:" ) + UniversalCRTLibPath)); |
130 | } |
131 | std::string WindowsSdkLibPath; |
132 | if (TC.getWindowsSDKLibraryPath(Args, path&: WindowsSdkLibPath)) |
133 | CmdArgs.push_back( |
134 | Elt: Args.MakeArgString(Str: std::string("-libpath:" ) + WindowsSdkLibPath)); |
135 | } |
136 | |
137 | if (C.getDriver().IsFlangMode()) { |
138 | addFortranRuntimeLibraryPath(TC, Args, CmdArgs); |
139 | addFortranRuntimeLibs(TC, Args, CmdArgs); |
140 | |
141 | // Inform the MSVC linker that we're generating a console application, i.e. |
142 | // one with `main` as the "user-defined" entry point. The `main` function is |
143 | // defined in flang's runtime libraries. |
144 | CmdArgs.push_back(Elt: "/subsystem:console" ); |
145 | } |
146 | |
147 | // Add the compiler-rt library directories to libpath if they exist to help |
148 | // the linker find the various sanitizer, builtin, and profiling runtimes. |
149 | for (const auto &LibPath : TC.getLibraryPaths()) { |
150 | if (TC.getVFS().exists(Path: LibPath)) |
151 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-libpath:" + LibPath)); |
152 | } |
153 | auto CRTPath = TC.getCompilerRTPath(); |
154 | if (TC.getVFS().exists(Path: CRTPath)) |
155 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-libpath:" + CRTPath)); |
156 | |
157 | if (!C.getDriver().IsCLMode() && Args.hasArg(options::OPT_L)) |
158 | for (const auto &LibPath : Args.getAllArgValues(options::OPT_L)) |
159 | CmdArgs.push_back(Args.MakeArgString("-libpath:" + LibPath)); |
160 | |
161 | CmdArgs.push_back(Elt: "-nologo" ); |
162 | |
163 | if (Args.hasArg(options::OPT_g_Group, options::OPT__SLASH_Z7)) |
164 | CmdArgs.push_back(Elt: "-debug" ); |
165 | |
166 | // If we specify /hotpatch, let the linker add padding in front of each |
167 | // function, like MSVC does. |
168 | if (Args.hasArg(options::OPT_fms_hotpatch, options::OPT__SLASH_hotpatch)) |
169 | CmdArgs.push_back(Elt: "-functionpadmin" ); |
170 | |
171 | // Pass on /Brepro if it was passed to the compiler. |
172 | // Note that /Brepro maps to -mno-incremental-linker-compatible. |
173 | bool DefaultIncrementalLinkerCompatible = |
174 | C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment(); |
175 | if (!Args.hasFlag(options::OPT_mincremental_linker_compatible, |
176 | options::OPT_mno_incremental_linker_compatible, |
177 | DefaultIncrementalLinkerCompatible)) |
178 | CmdArgs.push_back(Elt: "-Brepro" ); |
179 | |
180 | bool DLL = Args.hasArg(options::OPT__SLASH_LD, options::OPT__SLASH_LDd, |
181 | options::OPT_shared); |
182 | if (DLL) { |
183 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-dll" )); |
184 | |
185 | SmallString<128> ImplibName(Output.getFilename()); |
186 | llvm::sys::path::replace_extension(path&: ImplibName, extension: "lib" ); |
187 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: std::string("-implib:" ) + ImplibName)); |
188 | } |
189 | |
190 | if (TC.getSanitizerArgs(JobArgs: Args).needsFuzzer()) { |
191 | if (!Args.hasArg(options::OPT_shared)) |
192 | CmdArgs.push_back( |
193 | Elt: Args.MakeArgString(Str: std::string("-wholearchive:" ) + |
194 | TC.getCompilerRTArgString(Args, Component: "fuzzer" ))); |
195 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-debug" )); |
196 | // Prevent the linker from padding sections we use for instrumentation |
197 | // arrays. |
198 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-incremental:no" )); |
199 | } |
200 | |
201 | if (TC.getSanitizerArgs(JobArgs: Args).needsAsanRt()) { |
202 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-debug" )); |
203 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-incremental:no" )); |
204 | if (TC.getSanitizerArgs(JobArgs: Args).needsSharedRt() || |
205 | Args.hasArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd)) { |
206 | for (const auto &Lib : {"asan_dynamic" , "asan_dynamic_runtime_thunk" }) |
207 | CmdArgs.push_back(Elt: TC.getCompilerRTArgString(Args, Component: Lib)); |
208 | // Make sure the dynamic runtime thunk is not optimized out at link time |
209 | // to ensure proper SEH handling. |
210 | CmdArgs.push_back(Elt: Args.MakeArgString( |
211 | Str: TC.getArch() == llvm::Triple::x86 |
212 | ? "-include:___asan_seh_interceptor" |
213 | : "-include:__asan_seh_interceptor" )); |
214 | // Make sure the linker consider all object files from the dynamic runtime |
215 | // thunk. |
216 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: std::string("-wholearchive:" ) + |
217 | TC.getCompilerRT(Args, Component: "asan_dynamic_runtime_thunk" ))); |
218 | } else if (DLL) { |
219 | CmdArgs.push_back(Elt: TC.getCompilerRTArgString(Args, Component: "asan_dll_thunk" )); |
220 | } else { |
221 | for (const auto &Lib : {"asan" , "asan_cxx" }) { |
222 | CmdArgs.push_back(Elt: TC.getCompilerRTArgString(Args, Component: Lib)); |
223 | // Make sure the linker consider all object files from the static lib. |
224 | // This is necessary because instrumented dlls need access to all the |
225 | // interface exported by the static lib in the main executable. |
226 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: std::string("-wholearchive:" ) + |
227 | TC.getCompilerRT(Args, Component: Lib))); |
228 | } |
229 | } |
230 | } |
231 | |
232 | Args.AddAllArgValues(Output&: CmdArgs, options::Id0: OPT__SLASH_link); |
233 | |
234 | // Control Flow Guard checks |
235 | for (const Arg *A : Args.filtered(options::OPT__SLASH_guard)) { |
236 | StringRef GuardArgs = A->getValue(); |
237 | if (GuardArgs.equals_insensitive("cf" ) || |
238 | GuardArgs.equals_insensitive("cf,nochecks" )) { |
239 | // MSVC doesn't yet support the "nochecks" modifier. |
240 | CmdArgs.push_back("-guard:cf" ); |
241 | } else if (GuardArgs.equals_insensitive("cf-" )) { |
242 | CmdArgs.push_back("-guard:cf-" ); |
243 | } else if (GuardArgs.equals_insensitive("ehcont" )) { |
244 | CmdArgs.push_back("-guard:ehcont" ); |
245 | } else if (GuardArgs.equals_insensitive("ehcont-" )) { |
246 | CmdArgs.push_back("-guard:ehcont-" ); |
247 | } |
248 | } |
249 | |
250 | if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, |
251 | options::OPT_fno_openmp, false)) { |
252 | CmdArgs.push_back(Elt: "-nodefaultlib:vcomp.lib" ); |
253 | CmdArgs.push_back(Elt: "-nodefaultlib:vcompd.lib" ); |
254 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: std::string("-libpath:" ) + |
255 | TC.getDriver().Dir + "/../lib" )); |
256 | switch (TC.getDriver().getOpenMPRuntime(Args)) { |
257 | case Driver::OMPRT_OMP: |
258 | CmdArgs.push_back(Elt: "-defaultlib:libomp.lib" ); |
259 | break; |
260 | case Driver::OMPRT_IOMP5: |
261 | CmdArgs.push_back(Elt: "-defaultlib:libiomp5md.lib" ); |
262 | break; |
263 | case Driver::OMPRT_GOMP: |
264 | break; |
265 | case Driver::OMPRT_Unknown: |
266 | // Already diagnosed. |
267 | break; |
268 | } |
269 | } |
270 | |
271 | // Add compiler-rt lib in case if it was explicitly |
272 | // specified as an argument for --rtlib option. |
273 | if (!Args.hasArg(options::OPT_nostdlib)) { |
274 | AddRunTimeLibs(TC, D: TC.getDriver(), CmdArgs, Args); |
275 | } |
276 | |
277 | StringRef Linker = |
278 | Args.getLastArgValue(options::Id: OPT_fuse_ld_EQ, CLANG_DEFAULT_LINKER); |
279 | if (Linker.empty()) |
280 | Linker = "link" ; |
281 | // We need to translate 'lld' into 'lld-link'. |
282 | else if (Linker.equals_insensitive(RHS: "lld" )) |
283 | Linker = "lld-link" ; |
284 | |
285 | if (Linker == "lld-link" ) { |
286 | for (Arg *A : Args.filtered(options::OPT_vfsoverlay)) |
287 | CmdArgs.push_back( |
288 | Args.MakeArgString(std::string("/vfsoverlay:" ) + A->getValue())); |
289 | |
290 | if (C.getDriver().isUsingLTO() && |
291 | Args.hasFlag(options::OPT_gsplit_dwarf, options::OPT_gno_split_dwarf, |
292 | false)) |
293 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: Twine("/dwodir:" ) + |
294 | Output.getFilename() + "_dwo" )); |
295 | } |
296 | |
297 | // Add filenames, libraries, and other linker inputs. |
298 | for (const auto &Input : Inputs) { |
299 | if (Input.isFilename()) { |
300 | CmdArgs.push_back(Elt: Input.getFilename()); |
301 | continue; |
302 | } |
303 | |
304 | const Arg &A = Input.getInputArg(); |
305 | |
306 | // Render -l options differently for the MSVC linker. |
307 | if (A.getOption().matches(options::ID: OPT_l)) { |
308 | StringRef Lib = A.getValue(); |
309 | const char *LinkLibArg; |
310 | if (Lib.ends_with(Suffix: ".lib" )) |
311 | LinkLibArg = Args.MakeArgString(Str: Lib); |
312 | else |
313 | LinkLibArg = Args.MakeArgString(Str: Lib + ".lib" ); |
314 | CmdArgs.push_back(Elt: LinkLibArg); |
315 | continue; |
316 | } |
317 | |
318 | // Otherwise, this is some other kind of linker input option like -Wl, -z, |
319 | // or -L. Render it, even if MSVC doesn't understand it. |
320 | A.renderAsInput(Args, Output&: CmdArgs); |
321 | } |
322 | |
323 | addHIPRuntimeLibArgs(TC, C, Args, CmdArgs); |
324 | |
325 | TC.addProfileRTLibs(Args, CmdArgs); |
326 | |
327 | std::vector<const char *> Environment; |
328 | |
329 | // We need to special case some linker paths. In the case of the regular msvc |
330 | // linker, we need to use a special search algorithm. |
331 | llvm::SmallString<128> linkPath; |
332 | if (Linker.equals_insensitive(RHS: "link" )) { |
333 | // If we're using the MSVC linker, it's not sufficient to just use link |
334 | // from the program PATH, because other environments like GnuWin32 install |
335 | // their own link.exe which may come first. |
336 | linkPath = FindVisualStudioExecutable(TC, Exe: "link.exe" ); |
337 | |
338 | if (!TC.FoundMSVCInstall() && !canExecute(VFS&: TC.getVFS(), Path: linkPath)) { |
339 | llvm::SmallString<128> ClPath; |
340 | ClPath = TC.GetProgramPath(Name: "cl.exe" ); |
341 | if (canExecute(VFS&: TC.getVFS(), Path: ClPath)) { |
342 | linkPath = llvm::sys::path::parent_path(path: ClPath); |
343 | llvm::sys::path::append(path&: linkPath, a: "link.exe" ); |
344 | if (!canExecute(VFS&: TC.getVFS(), Path: linkPath)) |
345 | C.getDriver().Diag(clang::diag::DiagID: warn_drv_msvc_not_found); |
346 | } else { |
347 | C.getDriver().Diag(clang::diag::DiagID: warn_drv_msvc_not_found); |
348 | } |
349 | } |
350 | |
351 | // Clang handles passing the proper asan libs to the linker, which goes |
352 | // against link.exe's /INFERASANLIBS which automatically finds asan libs. |
353 | if (TC.getSanitizerArgs(JobArgs: Args).needsAsanRt()) |
354 | CmdArgs.push_back(Elt: "/INFERASANLIBS:NO" ); |
355 | |
356 | #ifdef _WIN32 |
357 | // When cross-compiling with VS2017 or newer, link.exe expects to have |
358 | // its containing bin directory at the top of PATH, followed by the |
359 | // native target bin directory. |
360 | // e.g. when compiling for x86 on an x64 host, PATH should start with: |
361 | // /bin/Hostx64/x86;/bin/Hostx64/x64 |
362 | // This doesn't attempt to handle llvm::ToolsetLayout::DevDivInternal. |
363 | if (TC.getIsVS2017OrNewer() && |
364 | llvm::Triple(llvm::sys::getProcessTriple()).getArch() != TC.getArch()) { |
365 | auto HostArch = llvm::Triple(llvm::sys::getProcessTriple()).getArch(); |
366 | |
367 | auto EnvBlockWide = |
368 | std::unique_ptr<wchar_t[], decltype(&FreeEnvironmentStringsW)>( |
369 | GetEnvironmentStringsW(), FreeEnvironmentStringsW); |
370 | if (!EnvBlockWide) |
371 | goto SkipSettingEnvironment; |
372 | |
373 | size_t EnvCount = 0; |
374 | size_t EnvBlockLen = 0; |
375 | while (EnvBlockWide[EnvBlockLen] != L'\0') { |
376 | ++EnvCount; |
377 | EnvBlockLen += std::wcslen(&EnvBlockWide[EnvBlockLen]) + |
378 | 1 /*string null-terminator*/; |
379 | } |
380 | ++EnvBlockLen; // add the block null-terminator |
381 | |
382 | std::string EnvBlock; |
383 | if (!llvm::convertUTF16ToUTF8String( |
384 | llvm::ArrayRef<char>(reinterpret_cast<char *>(EnvBlockWide.get()), |
385 | EnvBlockLen * sizeof(EnvBlockWide[0])), |
386 | EnvBlock)) |
387 | goto SkipSettingEnvironment; |
388 | |
389 | Environment.reserve(EnvCount); |
390 | |
391 | // Now loop over each string in the block and copy them into the |
392 | // environment vector, adjusting the PATH variable as needed when we |
393 | // find it. |
394 | for (const char *Cursor = EnvBlock.data(); *Cursor != '\0';) { |
395 | llvm::StringRef EnvVar(Cursor); |
396 | if (EnvVar.starts_with_insensitive("path=" )) { |
397 | constexpr size_t PrefixLen = 5; // strlen("path=") |
398 | Environment.push_back(Args.MakeArgString( |
399 | EnvVar.substr(0, PrefixLen) + |
400 | TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin) + |
401 | llvm::Twine(llvm::sys::EnvPathSeparator) + |
402 | TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin, HostArch) + |
403 | (EnvVar.size() > PrefixLen |
404 | ? llvm::Twine(llvm::sys::EnvPathSeparator) + |
405 | EnvVar.substr(PrefixLen) |
406 | : "" ))); |
407 | } else { |
408 | Environment.push_back(Args.MakeArgString(EnvVar)); |
409 | } |
410 | Cursor += EnvVar.size() + 1 /*null-terminator*/; |
411 | } |
412 | } |
413 | SkipSettingEnvironment:; |
414 | #endif |
415 | } else { |
416 | linkPath = TC.GetProgramPath(Name: Linker.str().c_str()); |
417 | } |
418 | |
419 | auto LinkCmd = std::make_unique<Command>( |
420 | args: JA, args: *this, args: ResponseFileSupport::AtFileUTF16(), |
421 | args: Args.MakeArgString(Str: linkPath), args&: CmdArgs, args: Inputs, args: Output); |
422 | if (!Environment.empty()) |
423 | LinkCmd->setEnvironment(Environment); |
424 | C.addCommand(C: std::move(LinkCmd)); |
425 | } |
426 | |
427 | MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple, |
428 | const ArgList &Args) |
429 | : ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args), |
430 | RocmInstallation(D, Triple, Args) { |
431 | getProgramPaths().push_back(Elt: getDriver().Dir); |
432 | |
433 | std::optional<llvm::StringRef> VCToolsDir, VCToolsVersion; |
434 | if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir)) |
435 | VCToolsDir = A->getValue(); |
436 | if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsversion)) |
437 | VCToolsVersion = A->getValue(); |
438 | if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir)) |
439 | WinSdkDir = A->getValue(); |
440 | if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion)) |
441 | WinSdkVersion = A->getValue(); |
442 | if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsysroot)) |
443 | WinSysRoot = A->getValue(); |
444 | |
445 | // Check the command line first, that's the user explicitly telling us what to |
446 | // use. Check the environment next, in case we're being invoked from a VS |
447 | // command prompt. Failing that, just try to find the newest Visual Studio |
448 | // version we can and use its default VC toolchain. |
449 | llvm::findVCToolChainViaCommandLine(VFS&: getVFS(), VCToolsDir, VCToolsVersion, |
450 | WinSysRoot, Path&: VCToolChainPath, VSLayout) || |
451 | llvm::findVCToolChainViaEnvironment(VFS&: getVFS(), Path&: VCToolChainPath, |
452 | VSLayout) || |
453 | llvm::findVCToolChainViaSetupConfig(VFS&: getVFS(), VCToolsVersion, |
454 | Path&: VCToolChainPath, VSLayout) || |
455 | llvm::findVCToolChainViaRegistry(Path&: VCToolChainPath, VSLayout); |
456 | } |
457 | |
458 | Tool *MSVCToolChain::buildLinker() const { |
459 | return new tools::visualstudio::Linker(*this); |
460 | } |
461 | |
462 | Tool *MSVCToolChain::buildAssembler() const { |
463 | if (getTriple().isOSBinFormatMachO()) |
464 | return new tools::darwin::Assembler(*this); |
465 | getDriver().Diag(clang::diag::DiagID: err_no_external_assembler); |
466 | return nullptr; |
467 | } |
468 | |
469 | ToolChain::UnwindTableLevel |
470 | MSVCToolChain::getDefaultUnwindTableLevel(const ArgList &Args) const { |
471 | // Don't emit unwind tables by default for MachO targets. |
472 | if (getTriple().isOSBinFormatMachO()) |
473 | return UnwindTableLevel::None; |
474 | |
475 | // All non-x86_32 Windows targets require unwind tables. However, LLVM |
476 | // doesn't know how to generate them for all targets, so only enable |
477 | // the ones that are actually implemented. |
478 | if (getArch() == llvm::Triple::x86_64 || getArch() == llvm::Triple::arm || |
479 | getArch() == llvm::Triple::thumb || getArch() == llvm::Triple::aarch64) |
480 | return UnwindTableLevel::Asynchronous; |
481 | |
482 | return UnwindTableLevel::None; |
483 | } |
484 | |
485 | bool MSVCToolChain::isPICDefault() const { |
486 | return getArch() == llvm::Triple::x86_64 || |
487 | getArch() == llvm::Triple::aarch64; |
488 | } |
489 | |
490 | bool MSVCToolChain::isPIEDefault(const llvm::opt::ArgList &Args) const { |
491 | return false; |
492 | } |
493 | |
494 | bool MSVCToolChain::isPICDefaultForced() const { |
495 | return getArch() == llvm::Triple::x86_64 || |
496 | getArch() == llvm::Triple::aarch64; |
497 | } |
498 | |
499 | void MSVCToolChain::AddCudaIncludeArgs(const ArgList &DriverArgs, |
500 | ArgStringList &CC1Args) const { |
501 | CudaInstallation->AddCudaIncludeArgs(DriverArgs, CC1Args); |
502 | } |
503 | |
504 | void MSVCToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs, |
505 | ArgStringList &CC1Args) const { |
506 | RocmInstallation->AddHIPIncludeArgs(DriverArgs, CC1Args); |
507 | } |
508 | |
509 | void MSVCToolChain::AddHIPRuntimeLibArgs(const ArgList &Args, |
510 | ArgStringList &CmdArgs) const { |
511 | CmdArgs.append(IL: {Args.MakeArgString(Str: StringRef("-libpath:" ) + |
512 | RocmInstallation->getLibPath()), |
513 | "amdhip64.lib" }); |
514 | } |
515 | |
516 | void MSVCToolChain::printVerboseInfo(raw_ostream &OS) const { |
517 | CudaInstallation->print(OS); |
518 | RocmInstallation->print(OS); |
519 | } |
520 | |
521 | std::string |
522 | MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type, |
523 | llvm::StringRef SubdirParent) const { |
524 | return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, TargetArch: getArch(), |
525 | SubdirParent); |
526 | } |
527 | |
528 | std::string |
529 | MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type, |
530 | llvm::Triple::ArchType TargetArch) const { |
531 | return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, TargetArch, |
532 | SubdirParent: "" ); |
533 | } |
534 | |
535 | // Find the most recent version of Universal CRT or Windows 10 SDK. |
536 | // vcvarsqueryregistry.bat from Visual Studio 2015 sorts entries in the include |
537 | // directory by name and uses the last one of the list. |
538 | // So we compare entry names lexicographically to find the greatest one. |
539 | // Gets the library path required to link against the Windows SDK. |
540 | bool MSVCToolChain::getWindowsSDKLibraryPath(const ArgList &Args, |
541 | std::string &path) const { |
542 | std::string sdkPath; |
543 | int sdkMajor = 0; |
544 | std::string windowsSDKIncludeVersion; |
545 | std::string windowsSDKLibVersion; |
546 | |
547 | path.clear(); |
548 | if (!llvm::getWindowsSDKDir(VFS&: getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot, |
549 | Path&: sdkPath, Major&: sdkMajor, WindowsSDKIncludeVersion&: windowsSDKIncludeVersion, |
550 | WindowsSDKLibVersion&: windowsSDKLibVersion)) |
551 | return false; |
552 | |
553 | llvm::SmallString<128> libPath(sdkPath); |
554 | llvm::sys::path::append(path&: libPath, a: "Lib" ); |
555 | if (sdkMajor >= 10) |
556 | if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) && |
557 | WinSdkVersion.has_value()) |
558 | windowsSDKLibVersion = *WinSdkVersion; |
559 | if (sdkMajor >= 8) |
560 | llvm::sys::path::append(path&: libPath, a: windowsSDKLibVersion, b: "um" ); |
561 | return llvm::appendArchToWindowsSDKLibPath(SDKMajor: sdkMajor, LibPath: libPath, Arch: getArch(), |
562 | path); |
563 | } |
564 | |
565 | bool MSVCToolChain::useUniversalCRT() const { |
566 | return llvm::useUniversalCRT(VSLayout, VCToolChainPath, TargetArch: getArch(), VFS&: getVFS()); |
567 | } |
568 | |
569 | bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args, |
570 | std::string &Path) const { |
571 | std::string UniversalCRTSdkPath; |
572 | std::string UCRTVersion; |
573 | |
574 | Path.clear(); |
575 | if (!llvm::getUniversalCRTSdkDir(VFS&: getVFS(), WinSdkDir, WinSdkVersion, |
576 | WinSysRoot, Path&: UniversalCRTSdkPath, |
577 | UCRTVersion)) |
578 | return false; |
579 | |
580 | if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) && |
581 | WinSdkVersion.has_value()) |
582 | UCRTVersion = *WinSdkVersion; |
583 | |
584 | StringRef ArchName = llvm::archToWindowsSDKArch(Arch: getArch()); |
585 | if (ArchName.empty()) |
586 | return false; |
587 | |
588 | llvm::SmallString<128> LibPath(UniversalCRTSdkPath); |
589 | llvm::sys::path::append(path&: LibPath, a: "Lib" , b: UCRTVersion, c: "ucrt" , d: ArchName); |
590 | |
591 | Path = std::string(LibPath); |
592 | return true; |
593 | } |
594 | |
595 | static VersionTuple getMSVCVersionFromExe(const std::string &BinDir) { |
596 | VersionTuple Version; |
597 | #ifdef _WIN32 |
598 | SmallString<128> ClExe(BinDir); |
599 | llvm::sys::path::append(ClExe, "cl.exe" ); |
600 | |
601 | std::wstring ClExeWide; |
602 | if (!llvm::ConvertUTF8toWide(ClExe.c_str(), ClExeWide)) |
603 | return Version; |
604 | |
605 | const DWORD VersionSize = ::GetFileVersionInfoSizeW(ClExeWide.c_str(), |
606 | nullptr); |
607 | if (VersionSize == 0) |
608 | return Version; |
609 | |
610 | SmallVector<uint8_t, 4 * 1024> VersionBlock(VersionSize); |
611 | if (!::GetFileVersionInfoW(ClExeWide.c_str(), 0, VersionSize, |
612 | VersionBlock.data())) |
613 | return Version; |
614 | |
615 | VS_FIXEDFILEINFO *FileInfo = nullptr; |
616 | UINT FileInfoSize = 0; |
617 | if (!::VerQueryValueW(VersionBlock.data(), L"\\" , |
618 | reinterpret_cast<LPVOID *>(&FileInfo), &FileInfoSize) || |
619 | FileInfoSize < sizeof(*FileInfo)) |
620 | return Version; |
621 | |
622 | const unsigned Major = (FileInfo->dwFileVersionMS >> 16) & 0xFFFF; |
623 | const unsigned Minor = (FileInfo->dwFileVersionMS ) & 0xFFFF; |
624 | const unsigned Micro = (FileInfo->dwFileVersionLS >> 16) & 0xFFFF; |
625 | |
626 | Version = VersionTuple(Major, Minor, Micro); |
627 | #endif |
628 | return Version; |
629 | } |
630 | |
631 | void MSVCToolChain::AddSystemIncludeWithSubfolder( |
632 | const ArgList &DriverArgs, ArgStringList &CC1Args, |
633 | const std::string &folder, const Twine &subfolder1, const Twine &subfolder2, |
634 | const Twine &subfolder3) const { |
635 | llvm::SmallString<128> path(folder); |
636 | llvm::sys::path::append(path, a: subfolder1, b: subfolder2, c: subfolder3); |
637 | addSystemInclude(DriverArgs, CC1Args, Path: path); |
638 | } |
639 | |
640 | void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, |
641 | ArgStringList &CC1Args) const { |
642 | if (DriverArgs.hasArg(options::OPT_nostdinc)) |
643 | return; |
644 | |
645 | if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { |
646 | AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, folder: getDriver().ResourceDir, |
647 | subfolder1: "include" ); |
648 | } |
649 | |
650 | // Add %INCLUDE%-like directories from the -imsvc flag. |
651 | for (const auto &Path : DriverArgs.getAllArgValues(options::OPT__SLASH_imsvc)) |
652 | addSystemInclude(DriverArgs, CC1Args, Path); |
653 | |
654 | auto AddSystemIncludesFromEnv = [&](StringRef Var) -> bool { |
655 | if (auto Val = llvm::sys::Process::GetEnv(name: Var)) { |
656 | SmallVector<StringRef, 8> Dirs; |
657 | StringRef(*Val).split(A&: Dirs, Separator: ";" , /*MaxSplit=*/-1, /*KeepEmpty=*/false); |
658 | if (!Dirs.empty()) { |
659 | addSystemIncludes(DriverArgs, CC1Args, Paths: Dirs); |
660 | return true; |
661 | } |
662 | } |
663 | return false; |
664 | }; |
665 | |
666 | // Add %INCLUDE%-like dirs via /external:env: flags. |
667 | for (const auto &Var : |
668 | DriverArgs.getAllArgValues(options::OPT__SLASH_external_env)) { |
669 | AddSystemIncludesFromEnv(Var); |
670 | } |
671 | |
672 | // Add DIA SDK include if requested. |
673 | if (const Arg *A = DriverArgs.getLastArg(options::OPT__SLASH_diasdkdir, |
674 | options::OPT__SLASH_winsysroot)) { |
675 | // cl.exe doesn't find the DIA SDK automatically, so this too requires |
676 | // explicit flags and doesn't automatically look in "DIA SDK" relative |
677 | // to the path we found for VCToolChainPath. |
678 | llvm::SmallString<128> DIASDKPath(A->getValue()); |
679 | if (A->getOption().getID() == options::OPT__SLASH_winsysroot) |
680 | llvm::sys::path::append(path&: DIASDKPath, a: "DIA SDK" ); |
681 | AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, folder: std::string(DIASDKPath), |
682 | subfolder1: "include" ); |
683 | } |
684 | |
685 | if (DriverArgs.hasArg(options::OPT_nostdlibinc)) |
686 | return; |
687 | |
688 | // Honor %INCLUDE% and %EXTERNAL_INCLUDE%. It should have essential search |
689 | // paths set by vcvarsall.bat. Skip if the user expressly set a vctoolsdir. |
690 | if (!DriverArgs.getLastArg(options::OPT__SLASH_vctoolsdir, |
691 | options::OPT__SLASH_winsysroot)) { |
692 | bool Found = AddSystemIncludesFromEnv("INCLUDE" ); |
693 | Found |= AddSystemIncludesFromEnv("EXTERNAL_INCLUDE" ); |
694 | if (Found) |
695 | return; |
696 | } |
697 | |
698 | // When built with access to the proper Windows APIs, try to actually find |
699 | // the correct include paths first. |
700 | if (!VCToolChainPath.empty()) { |
701 | addSystemInclude(DriverArgs, CC1Args, |
702 | Path: getSubDirectoryPath(Type: llvm::SubDirectoryType::Include)); |
703 | addSystemInclude( |
704 | DriverArgs, CC1Args, |
705 | Path: getSubDirectoryPath(Type: llvm::SubDirectoryType::Include, SubdirParent: "atlmfc" )); |
706 | |
707 | if (useUniversalCRT()) { |
708 | std::string UniversalCRTSdkPath; |
709 | std::string UCRTVersion; |
710 | if (llvm::getUniversalCRTSdkDir(VFS&: getVFS(), WinSdkDir, WinSdkVersion, |
711 | WinSysRoot, Path&: UniversalCRTSdkPath, |
712 | UCRTVersion)) { |
713 | if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) && |
714 | WinSdkVersion.has_value()) |
715 | UCRTVersion = *WinSdkVersion; |
716 | AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, folder: UniversalCRTSdkPath, |
717 | subfolder1: "Include" , subfolder2: UCRTVersion, subfolder3: "ucrt" ); |
718 | } |
719 | } |
720 | |
721 | std::string WindowsSDKDir; |
722 | int major = 0; |
723 | std::string windowsSDKIncludeVersion; |
724 | std::string windowsSDKLibVersion; |
725 | if (llvm::getWindowsSDKDir(VFS&: getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot, |
726 | Path&: WindowsSDKDir, Major&: major, WindowsSDKIncludeVersion&: windowsSDKIncludeVersion, |
727 | WindowsSDKLibVersion&: windowsSDKLibVersion)) { |
728 | if (major >= 10) |
729 | if (!(WinSdkDir.has_value() || WinSysRoot.has_value()) && |
730 | WinSdkVersion.has_value()) |
731 | windowsSDKIncludeVersion = windowsSDKLibVersion = *WinSdkVersion; |
732 | if (major >= 8) { |
733 | // Note: windowsSDKIncludeVersion is empty for SDKs prior to v10. |
734 | // Anyway, llvm::sys::path::append is able to manage it. |
735 | AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, folder: WindowsSDKDir, |
736 | subfolder1: "Include" , subfolder2: windowsSDKIncludeVersion, |
737 | subfolder3: "shared" ); |
738 | AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, folder: WindowsSDKDir, |
739 | subfolder1: "Include" , subfolder2: windowsSDKIncludeVersion, |
740 | subfolder3: "um" ); |
741 | AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, folder: WindowsSDKDir, |
742 | subfolder1: "Include" , subfolder2: windowsSDKIncludeVersion, |
743 | subfolder3: "winrt" ); |
744 | if (major >= 10) { |
745 | llvm::VersionTuple Tuple; |
746 | if (!Tuple.tryParse(string: windowsSDKIncludeVersion) && |
747 | Tuple.getSubminor().value_or(u: 0) >= 17134) { |
748 | AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, folder: WindowsSDKDir, |
749 | subfolder1: "Include" , subfolder2: windowsSDKIncludeVersion, |
750 | subfolder3: "cppwinrt" ); |
751 | } |
752 | } |
753 | } else { |
754 | AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, folder: WindowsSDKDir, |
755 | subfolder1: "Include" ); |
756 | } |
757 | } |
758 | |
759 | return; |
760 | } |
761 | |
762 | #if defined(_WIN32) |
763 | // As a fallback, select default install paths. |
764 | // FIXME: Don't guess drives and paths like this on Windows. |
765 | const StringRef Paths[] = { |
766 | "C:/Program Files/Microsoft Visual Studio 10.0/VC/include" , |
767 | "C:/Program Files/Microsoft Visual Studio 9.0/VC/include" , |
768 | "C:/Program Files/Microsoft Visual Studio 9.0/VC/PlatformSDK/Include" , |
769 | "C:/Program Files/Microsoft Visual Studio 8/VC/include" , |
770 | "C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include" |
771 | }; |
772 | addSystemIncludes(DriverArgs, CC1Args, Paths); |
773 | #endif |
774 | } |
775 | |
776 | void MSVCToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, |
777 | ArgStringList &CC1Args) const { |
778 | // FIXME: There should probably be logic here to find libc++ on Windows. |
779 | } |
780 | |
781 | VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D, |
782 | const ArgList &Args) const { |
783 | bool IsWindowsMSVC = getTriple().isWindowsMSVCEnvironment(); |
784 | VersionTuple MSVT = ToolChain::computeMSVCVersion(D, Args); |
785 | if (MSVT.empty()) |
786 | MSVT = getTriple().getEnvironmentVersion(); |
787 | if (MSVT.empty() && IsWindowsMSVC) |
788 | MSVT = |
789 | getMSVCVersionFromExe(BinDir: getSubDirectoryPath(Type: llvm::SubDirectoryType::Bin)); |
790 | if (MSVT.empty() && |
791 | Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions, |
792 | IsWindowsMSVC)) { |
793 | // -fms-compatibility-version=19.33 is default, aka 2022, 17.3 |
794 | // NOTE: when changing this value, also update |
795 | // clang/docs/CommandGuide/clang.rst and clang/docs/UsersManual.rst |
796 | // accordingly. |
797 | MSVT = VersionTuple(19, 33); |
798 | } |
799 | return MSVT; |
800 | } |
801 | |
802 | std::string |
803 | MSVCToolChain::ComputeEffectiveClangTriple(const ArgList &Args, |
804 | types::ID InputType) const { |
805 | // The MSVC version doesn't care about the architecture, even though it |
806 | // may look at the triple internally. |
807 | VersionTuple MSVT = computeMSVCVersion(/*D=*/nullptr, Args); |
808 | MSVT = VersionTuple(MSVT.getMajor(), MSVT.getMinor().value_or(u: 0), |
809 | MSVT.getSubminor().value_or(u: 0)); |
810 | |
811 | // For the rest of the triple, however, a computed architecture name may |
812 | // be needed. |
813 | llvm::Triple Triple(ToolChain::ComputeEffectiveClangTriple(Args, InputType)); |
814 | if (Triple.getEnvironment() == llvm::Triple::MSVC) { |
815 | StringRef ObjFmt = Triple.getEnvironmentName().split(Separator: '-').second; |
816 | if (ObjFmt.empty()) |
817 | Triple.setEnvironmentName((Twine("msvc" ) + MSVT.getAsString()).str()); |
818 | else |
819 | Triple.setEnvironmentName( |
820 | (Twine("msvc" ) + MSVT.getAsString() + Twine('-') + ObjFmt).str()); |
821 | } |
822 | return Triple.getTriple(); |
823 | } |
824 | |
825 | SanitizerMask MSVCToolChain::getSupportedSanitizers() const { |
826 | SanitizerMask Res = ToolChain::getSupportedSanitizers(); |
827 | Res |= SanitizerKind::Address; |
828 | Res |= SanitizerKind::PointerCompare; |
829 | Res |= SanitizerKind::PointerSubtract; |
830 | Res |= SanitizerKind::Fuzzer; |
831 | Res |= SanitizerKind::FuzzerNoLink; |
832 | Res &= ~SanitizerKind::CFIMFCall; |
833 | return Res; |
834 | } |
835 | |
836 | static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL, |
837 | bool SupportsForcingFramePointer, |
838 | const char *ExpandChar, const OptTable &Opts) { |
839 | assert(A->getOption().matches(options::OPT__SLASH_O)); |
840 | |
841 | StringRef OptStr = A->getValue(); |
842 | for (size_t I = 0, E = OptStr.size(); I != E; ++I) { |
843 | const char &OptChar = *(OptStr.data() + I); |
844 | switch (OptChar) { |
845 | default: |
846 | break; |
847 | case '1': |
848 | case '2': |
849 | case 'x': |
850 | case 'd': |
851 | // Ignore /O[12xd] flags that aren't the last one on the command line. |
852 | // Only the last one gets expanded. |
853 | if (&OptChar != ExpandChar) { |
854 | A->claim(); |
855 | break; |
856 | } |
857 | if (OptChar == 'd') { |
858 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0)); |
859 | } else { |
860 | if (OptChar == '1') { |
861 | DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s" ); |
862 | } else if (OptChar == '2' || OptChar == 'x') { |
863 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin)); |
864 | DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "2" ); |
865 | } |
866 | if (SupportsForcingFramePointer && |
867 | !DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer)) |
868 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer)); |
869 | if (OptChar == '1' || OptChar == '2') |
870 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_ffunction_sections)); |
871 | } |
872 | break; |
873 | case 'b': |
874 | if (I + 1 != E && isdigit(OptStr[I + 1])) { |
875 | switch (OptStr[I + 1]) { |
876 | case '0': |
877 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline)); |
878 | break; |
879 | case '1': |
880 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_hint_functions)); |
881 | break; |
882 | case '2': |
883 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions)); |
884 | break; |
885 | } |
886 | ++I; |
887 | } |
888 | break; |
889 | case 'g': |
890 | A->claim(); |
891 | break; |
892 | case 'i': |
893 | if (I + 1 != E && OptStr[I + 1] == '-') { |
894 | ++I; |
895 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin)); |
896 | } else { |
897 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin)); |
898 | } |
899 | break; |
900 | case 's': |
901 | DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s" ); |
902 | break; |
903 | case 't': |
904 | DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "2" ); |
905 | break; |
906 | case 'y': { |
907 | bool OmitFramePointer = true; |
908 | if (I + 1 != E && OptStr[I + 1] == '-') { |
909 | OmitFramePointer = false; |
910 | ++I; |
911 | } |
912 | if (SupportsForcingFramePointer) { |
913 | if (OmitFramePointer) |
914 | DAL.AddFlagArg(A, |
915 | Opts.getOption(options::OPT_fomit_frame_pointer)); |
916 | else |
917 | DAL.AddFlagArg( |
918 | A, Opts.getOption(options::OPT_fno_omit_frame_pointer)); |
919 | } else { |
920 | // Don't warn about /Oy- in x86-64 builds (where |
921 | // SupportsForcingFramePointer is false). The flag having no effect |
922 | // there is a compiler-internal optimization, and people shouldn't have |
923 | // to special-case their build files for x86-64 clang-cl. |
924 | A->claim(); |
925 | } |
926 | break; |
927 | } |
928 | } |
929 | } |
930 | } |
931 | |
932 | static void TranslateDArg(Arg *A, llvm::opt::DerivedArgList &DAL, |
933 | const OptTable &Opts) { |
934 | assert(A->getOption().matches(options::OPT_D)); |
935 | |
936 | StringRef Val = A->getValue(); |
937 | size_t Hash = Val.find(C: '#'); |
938 | if (Hash == StringRef::npos || Hash > Val.find(C: '=')) { |
939 | DAL.append(A); |
940 | return; |
941 | } |
942 | |
943 | std::string NewVal = std::string(Val); |
944 | NewVal[Hash] = '='; |
945 | DAL.AddJoinedArg(A, Opts.getOption(options::OPT_D), NewVal); |
946 | } |
947 | |
948 | static void TranslatePermissive(Arg *A, llvm::opt::DerivedArgList &DAL, |
949 | const OptTable &Opts) { |
950 | DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase_)); |
951 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_operator_names)); |
952 | } |
953 | |
954 | static void TranslatePermissiveMinus(Arg *A, llvm::opt::DerivedArgList &DAL, |
955 | const OptTable &Opts) { |
956 | DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase)); |
957 | DAL.AddFlagArg(A, Opts.getOption(options::OPT_foperator_names)); |
958 | } |
959 | |
960 | llvm::opt::DerivedArgList * |
961 | MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args, |
962 | StringRef BoundArch, |
963 | Action::OffloadKind OFK) const { |
964 | DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs()); |
965 | const OptTable &Opts = getDriver().getOpts(); |
966 | |
967 | // /Oy and /Oy- don't have an effect on X86-64 |
968 | bool SupportsForcingFramePointer = getArch() != llvm::Triple::x86_64; |
969 | |
970 | // The -O[12xd] flag actually expands to several flags. We must desugar the |
971 | // flags so that options embedded can be negated. For example, the '-O2' flag |
972 | // enables '-Oy'. Expanding '-O2' into its constituent flags allows us to |
973 | // correctly handle '-O2 -Oy-' where the trailing '-Oy-' disables a single |
974 | // aspect of '-O2'. |
975 | // |
976 | // Note that this expansion logic only applies to the *last* of '[12xd]'. |
977 | |
978 | // First step is to search for the character we'd like to expand. |
979 | const char *ExpandChar = nullptr; |
980 | for (Arg *A : Args.filtered(options::OPT__SLASH_O)) { |
981 | StringRef OptStr = A->getValue(); |
982 | for (size_t I = 0, E = OptStr.size(); I != E; ++I) { |
983 | char OptChar = OptStr[I]; |
984 | char PrevChar = I > 0 ? OptStr[I - 1] : '0'; |
985 | if (PrevChar == 'b') { |
986 | // OptChar does not expand; it's an argument to the previous char. |
987 | continue; |
988 | } |
989 | if (OptChar == '1' || OptChar == '2' || OptChar == 'x' || OptChar == 'd') |
990 | ExpandChar = OptStr.data() + I; |
991 | } |
992 | } |
993 | |
994 | for (Arg *A : Args) { |
995 | if (A->getOption().matches(options::OPT__SLASH_O)) { |
996 | // The -O flag actually takes an amalgam of other options. For example, |
997 | // '/Ogyb2' is equivalent to '/Og' '/Oy' '/Ob2'. |
998 | TranslateOptArg(A, DAL&: *DAL, SupportsForcingFramePointer, ExpandChar, Opts); |
999 | } else if (A->getOption().matches(options::OPT_D)) { |
1000 | // Translate -Dfoo#bar into -Dfoo=bar. |
1001 | TranslateDArg(A, DAL&: *DAL, Opts); |
1002 | } else if (A->getOption().matches(options::OPT__SLASH_permissive)) { |
1003 | // Expand /permissive |
1004 | TranslatePermissive(A, DAL&: *DAL, Opts); |
1005 | } else if (A->getOption().matches(options::OPT__SLASH_permissive_)) { |
1006 | // Expand /permissive- |
1007 | TranslatePermissiveMinus(A, DAL&: *DAL, Opts); |
1008 | } else if (OFK != Action::OFK_HIP) { |
1009 | // HIP Toolchain translates input args by itself. |
1010 | DAL->append(A); |
1011 | } |
1012 | } |
1013 | |
1014 | return DAL; |
1015 | } |
1016 | |
1017 | void MSVCToolChain::addClangTargetOptions( |
1018 | const ArgList &DriverArgs, ArgStringList &CC1Args, |
1019 | Action::OffloadKind DeviceOffloadKind) const { |
1020 | // MSVC STL kindly allows removing all usages of typeid by defining |
1021 | // _HAS_STATIC_RTTI to 0. Do so, when compiling with -fno-rtti |
1022 | if (DriverArgs.hasFlag(options::OPT_fno_rtti, options::OPT_frtti, |
1023 | /*Default=*/false)) |
1024 | CC1Args.push_back(Elt: "-D_HAS_STATIC_RTTI=0" ); |
1025 | |
1026 | if (Arg *A = DriverArgs.getLastArgNoClaim(options::OPT_marm64x)) |
1027 | A->ignoreTargetSpecific(); |
1028 | } |
1029 | |