1//===--- HLSL.cpp - HLSL ToolChain Implementations --------------*- C++ -*-===//
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 "HLSL.h"
10#include "clang/Driver/CommonArgs.h"
11#include "clang/Driver/Compilation.h"
12#include "clang/Driver/Job.h"
13#include "llvm/ADT/StringSwitch.h"
14#include "llvm/TargetParser/Triple.h"
15#include <regex>
16
17using namespace clang::driver;
18using namespace clang::driver::tools;
19using namespace clang::driver::toolchains;
20using namespace clang;
21using namespace llvm::opt;
22using namespace llvm;
23
24namespace {
25
26const unsigned OfflineLibMinor = 0xF;
27
28bool isLegalShaderModel(Triple &T) {
29 if (T.getOS() != Triple::OSType::ShaderModel)
30 return false;
31
32 auto Version = T.getOSVersion();
33 if (Version.getBuild())
34 return false;
35 if (Version.getSubminor())
36 return false;
37
38 auto Kind = T.getEnvironment();
39
40 switch (Kind) {
41 default:
42 return false;
43 case Triple::EnvironmentType::Vertex:
44 case Triple::EnvironmentType::Hull:
45 case Triple::EnvironmentType::Domain:
46 case Triple::EnvironmentType::Geometry:
47 case Triple::EnvironmentType::Pixel:
48 case Triple::EnvironmentType::Compute: {
49 VersionTuple MinVer(4, 0);
50 return MinVer <= Version;
51 } break;
52 case Triple::EnvironmentType::Library: {
53 VersionTuple SM6x(6, OfflineLibMinor);
54 if (Version == SM6x)
55 return true;
56
57 VersionTuple MinVer(6, 3);
58 return MinVer <= Version;
59 } break;
60 case Triple::EnvironmentType::Amplification:
61 case Triple::EnvironmentType::Mesh: {
62 VersionTuple MinVer(6, 5);
63 return MinVer <= Version;
64 } break;
65 case Triple::EnvironmentType::RootSignature:
66 VersionTuple MinVer(1, 0);
67 VersionTuple MaxVer(1, 2);
68 return MinVer <= Version && Version <= MaxVer;
69 }
70 return false;
71}
72
73std::optional<llvm::Triple> tryParseTriple(StringRef Profile) {
74 // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
75 SmallVector<StringRef, 3> Parts;
76 Profile.split(A&: Parts, Separator: "_");
77 if (Parts.size() != 3)
78 return std::nullopt;
79
80 Triple::EnvironmentType Kind =
81 StringSwitch<Triple::EnvironmentType>(Parts[0])
82 .Case(S: "ps", Value: Triple::EnvironmentType::Pixel)
83 .Case(S: "vs", Value: Triple::EnvironmentType::Vertex)
84 .Case(S: "gs", Value: Triple::EnvironmentType::Geometry)
85 .Case(S: "hs", Value: Triple::EnvironmentType::Hull)
86 .Case(S: "ds", Value: Triple::EnvironmentType::Domain)
87 .Case(S: "cs", Value: Triple::EnvironmentType::Compute)
88 .Case(S: "lib", Value: Triple::EnvironmentType::Library)
89 .Case(S: "ms", Value: Triple::EnvironmentType::Mesh)
90 .Case(S: "as", Value: Triple::EnvironmentType::Amplification)
91 .Case(S: "rootsig", Value: Triple::EnvironmentType::RootSignature)
92 .Default(Value: Triple::EnvironmentType::UnknownEnvironment);
93 if (Kind == Triple::EnvironmentType::UnknownEnvironment)
94 return std::nullopt;
95
96 unsigned long long Major = 0;
97 if (llvm::getAsUnsignedInteger(Str: Parts[1], Radix: 0, Result&: Major))
98 return std::nullopt;
99
100 unsigned long long Minor = 0;
101 if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library)
102 Minor = OfflineLibMinor;
103 else if (llvm::getAsUnsignedInteger(Str: Parts[2], Radix: 0, Result&: Minor))
104 return std::nullopt;
105
106 // Determine DXIL version using the minor version number of Shader
107 // Model version specified in target profile. Prior to decoupling DXIL version
108 // numbering from that of Shader Model DXIL version 1.Y corresponds to SM 6.Y.
109 // E.g., dxilv1.Y-unknown-shadermodelX.Y-hull
110 llvm::Triple T;
111 Triple::SubArchType SubArch = llvm::Triple::NoSubArch;
112 switch (Minor) {
113 case 0:
114 SubArch = llvm::Triple::DXILSubArch_v1_0;
115 break;
116 case 1:
117 SubArch = llvm::Triple::DXILSubArch_v1_1;
118 break;
119 case 2:
120 SubArch = llvm::Triple::DXILSubArch_v1_2;
121 break;
122 case 3:
123 SubArch = llvm::Triple::DXILSubArch_v1_3;
124 break;
125 case 4:
126 SubArch = llvm::Triple::DXILSubArch_v1_4;
127 break;
128 case 5:
129 SubArch = llvm::Triple::DXILSubArch_v1_5;
130 break;
131 case 6:
132 SubArch = llvm::Triple::DXILSubArch_v1_6;
133 break;
134 case 7:
135 SubArch = llvm::Triple::DXILSubArch_v1_7;
136 break;
137 case 8:
138 SubArch = llvm::Triple::DXILSubArch_v1_8;
139 break;
140 case 9:
141 SubArch = llvm::Triple::DXILSubArch_v1_9;
142 break;
143 case OfflineLibMinor:
144 // Always consider minor version x as the latest supported DXIL version
145 SubArch = llvm::Triple::LatestDXILSubArch;
146 break;
147 default:
148 // No DXIL Version corresponding to specified Shader Model version found
149 return std::nullopt;
150 }
151 T.setArch(Kind: Triple::ArchType::dxil, SubArch);
152 T.setOSName(Triple::getOSTypeName(Kind: Triple::OSType::ShaderModel).str() +
153 VersionTuple(Major, Minor).getAsString());
154 T.setEnvironment(Kind);
155
156 return T;
157}
158
159std::optional<std::string> tryParseProfile(StringRef Profile) {
160 std::optional<llvm::Triple> MaybeT = tryParseTriple(Profile);
161 if (MaybeT && isLegalShaderModel(T&: *MaybeT))
162 return MaybeT->getTriple();
163 else
164 return std::nullopt;
165}
166
167bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) {
168 VersionTuple Version;
169 if (Version.tryParse(string: ValVersionStr) || Version.getBuild() ||
170 Version.getSubminor() || !Version.getMinor()) {
171 D.Diag(DiagID: diag::err_drv_invalid_format_dxil_validator_version)
172 << ValVersionStr;
173 return false;
174 }
175
176 uint64_t Major = Version.getMajor();
177 uint64_t Minor = *Version.getMinor();
178 if (Major == 0 && Minor != 0) {
179 D.Diag(DiagID: diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr;
180 return false;
181 }
182 VersionTuple MinVer(1, 0);
183 if (Version < MinVer) {
184 D.Diag(DiagID: diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr;
185 return false;
186 }
187 return true;
188}
189
190void getSpirvExtOperand(StringRef SpvExtensionArg, raw_ostream &out) {
191 // The extensions that are commented out are supported in DXC, but the SPIR-V
192 // backend does not know about them yet.
193 static const std::vector<StringRef> DxcSupportedExtensions = {
194 "SPV_KHR_16bit_storage",
195 "SPV_KHR_device_group",
196 "SPV_KHR_fragment_shading_rate",
197 "SPV_KHR_multiview",
198 "SPV_KHR_post_depth_coverage",
199 "SPV_KHR_non_semantic_info",
200 "SPV_KHR_shader_draw_parameters",
201 "SPV_KHR_ray_tracing",
202 "SPV_KHR_shader_clock",
203 "SPV_EXT_demote_to_helper_invocation",
204 "SPV_EXT_descriptor_indexing",
205 "SPV_EXT_fragment_fully_covered",
206 "SPV_EXT_fragment_invocation_density",
207 "SPV_EXT_fragment_shader_interlock",
208 "SPV_EXT_mesh_shader",
209 "SPV_EXT_shader_stencil_export",
210 "SPV_EXT_shader_viewport_index_layer",
211 // "SPV_AMD_shader_early_and_late_fragment_tests",
212 "SPV_GOOGLE_hlsl_functionality1",
213 "SPV_GOOGLE_user_type",
214 "SPV_KHR_ray_query",
215 "SPV_EXT_shader_image_int64",
216 "SPV_KHR_fragment_shader_barycentric",
217 "SPV_KHR_physical_storage_buffer",
218 "SPV_KHR_vulkan_memory_model",
219 // "SPV_KHR_compute_shader_derivatives",
220 "SPV_KHR_maximal_reconvergence",
221 "SPV_KHR_float_controls",
222 "SPV_NV_shader_subgroup_partitioned",
223 // "SPV_KHR_quad_control"
224 };
225
226 if (SpvExtensionArg.starts_with(Prefix: "SPV_")) {
227 out << "+" << SpvExtensionArg;
228 return;
229 }
230
231 if (SpvExtensionArg.compare_insensitive(RHS: "DXC") == 0) {
232 bool first = true;
233 for (StringRef E : DxcSupportedExtensions) {
234 if (!first)
235 out << ",";
236 else
237 first = false;
238 out << "+" << E;
239 }
240 return;
241 }
242 out << SpvExtensionArg;
243}
244
245SmallString<1024> getSpirvExtArg(ArrayRef<std::string> SpvExtensionArgs) {
246 if (SpvExtensionArgs.empty()) {
247 return StringRef("-spirv-ext=all");
248 }
249
250 llvm::SmallString<1024> LlvmOption;
251 raw_svector_ostream out(LlvmOption);
252
253 out << "-spirv-ext=";
254 getSpirvExtOperand(SpvExtensionArg: SpvExtensionArgs[0], out);
255
256 SpvExtensionArgs = SpvExtensionArgs.slice(N: 1);
257 for (StringRef Extension : SpvExtensionArgs) {
258 out << ",";
259 getSpirvExtOperand(SpvExtensionArg: Extension, out);
260 }
261 return LlvmOption;
262}
263
264bool isValidSPIRVExtensionName(const std::string &str) {
265 std::regex pattern("dxc|DXC|khr|KHR|SPV_[a-zA-Z0-9_]+");
266 return std::regex_match(s: str, re: pattern);
267}
268
269// SPIRV extension names are of the form `SPV_[a-zA-Z0-9_]+`. We want to
270// disallow obviously invalid names to avoid issues when parsing `spirv-ext`.
271bool checkExtensionArgsAreValid(ArrayRef<std::string> SpvExtensionArgs,
272 const Driver &Driver) {
273 bool AllValid = true;
274 for (auto Extension : SpvExtensionArgs) {
275 if (!isValidSPIRVExtensionName(str: Extension)) {
276 Driver.Diag(DiagID: diag::err_drv_invalid_value)
277 << "-fspv-extension" << Extension;
278 AllValid = false;
279 }
280 }
281 return AllValid;
282}
283
284bool isRootSignatureTarget(StringRef Profile) {
285 if (std::optional<llvm::Triple> T = tryParseTriple(Profile))
286 return T->getEnvironment() == Triple::EnvironmentType::RootSignature;
287 return false;
288}
289
290bool isRootSignatureTarget(DerivedArgList &Args) {
291 if (const Arg *A = Args.getLastArg(Ids: options::OPT_target_profile))
292 return isRootSignatureTarget(Profile: A->getValue());
293 return false;
294}
295
296} // namespace
297
298void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
299 const InputInfo &Output,
300 const InputInfoList &Inputs,
301 const ArgList &Args,
302 const char *LinkingOutput) const {
303 std::string DxvPath = getToolChain().GetProgramPath(Name: "dxv");
304 assert(DxvPath != "dxv" && "cannot find dxv");
305
306 ArgStringList CmdArgs;
307 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
308 const InputInfo &Input = Inputs[0];
309 CmdArgs.push_back(Elt: Input.getFilename());
310 CmdArgs.push_back(Elt: "-o");
311 CmdArgs.push_back(Elt: Output.getFilename());
312
313 const char *Exec = Args.MakeArgString(Str: DxvPath);
314 C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(),
315 args&: Exec, args&: CmdArgs, args: Inputs, args: Input));
316}
317
318void tools::hlsl::MetalConverter::ConstructJob(
319 Compilation &C, const JobAction &JA, const InputInfo &Output,
320 const InputInfoList &Inputs, const ArgList &Args,
321 const char *LinkingOutput) const {
322 std::string MSCPath = getToolChain().GetProgramPath(Name: "metal-shaderconverter");
323 ArgStringList CmdArgs;
324 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
325 const InputInfo &Input = Inputs[0];
326 CmdArgs.push_back(Elt: Input.getFilename());
327 CmdArgs.push_back(Elt: "-o");
328 CmdArgs.push_back(Elt: Output.getFilename());
329
330 StringRef Reflection = Args.getLastArgValue(Id: options::OPT_dxc_Fre);
331 if (!Reflection.empty()) {
332 const char *ReflectionStr =
333 Args.MakeArgString(Str: StringRef("--output-reflection-file=") + Reflection);
334 CmdArgs.push_back(Elt: ReflectionStr);
335 }
336
337 const char *Exec = Args.MakeArgString(Str: MSCPath);
338 C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(),
339 args&: Exec, args&: CmdArgs, args: Inputs, args: Input));
340}
341
342void tools::hlsl::LLVMObjcopy::ConstructJob(Compilation &C, const JobAction &JA,
343 const InputInfo &Output,
344 const InputInfoList &Inputs,
345 const ArgList &Args,
346 const char *LinkingOutput) const {
347
348 std::string ObjcopyPath = getToolChain().GetProgramPath(Name: "llvm-objcopy");
349 const char *Exec = Args.MakeArgString(Str: ObjcopyPath);
350
351 ArgStringList CmdArgs;
352 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
353 const InputInfo &Input = Inputs[0];
354 CmdArgs.push_back(Elt: Input.getFilename());
355 CmdArgs.push_back(Elt: Output.getFilename());
356
357 if (Args.hasArg(Ids: options::OPT_dxc_strip_rootsignature)) {
358 const char *StripRS = Args.MakeArgString(Str: "--remove-section=RTS0");
359 CmdArgs.push_back(Elt: StripRS);
360 }
361
362 if (Arg *Arg = Args.getLastArg(Ids: options::OPT_dxc_Frs)) {
363 const char *Frs =
364 Args.MakeArgString(Str: "--extract-section=RTS0=" + Twine(Arg->getValue()));
365 CmdArgs.push_back(Elt: Frs);
366 }
367
368 if (const Arg *A = Args.getLastArg(Ids: options::OPT_target_profile))
369 if (isRootSignatureTarget(Profile: A->getValue())) {
370 const char *Fos = Args.MakeArgString(Str: "--only-section=RTS0");
371 CmdArgs.push_back(Elt: Fos);
372 }
373
374 assert(CmdArgs.size() > 2 && "Unnecessary invocation of objcopy.");
375
376 C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(),
377 args&: Exec, args&: CmdArgs, args: Inputs, args: Input));
378}
379
380/// DirectX Toolchain
381HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
382 const ArgList &Args)
383 : ToolChain(D, Triple, Args) {
384 if (Args.hasArg(Ids: options::OPT_dxc_validator_path_EQ))
385 getProgramPaths().push_back(
386 Elt: Args.getLastArgValue(Id: options::OPT_dxc_validator_path_EQ).str());
387}
388
389Tool *clang::driver::toolchains::HLSLToolChain::getTool(
390 Action::ActionClass AC) const {
391 switch (AC) {
392 case Action::BinaryAnalyzeJobClass:
393 if (!Validator)
394 Validator.reset(p: new tools::hlsl::Validator(*this));
395 return Validator.get();
396 case Action::BinaryTranslatorJobClass:
397 if (!MetalConverter)
398 MetalConverter.reset(p: new tools::hlsl::MetalConverter(*this));
399 return MetalConverter.get();
400 case Action::ObjcopyJobClass:
401 if (!LLVMObjcopy)
402 LLVMObjcopy.reset(p: new tools::hlsl::LLVMObjcopy(*this));
403 return LLVMObjcopy.get();
404 default:
405 return ToolChain::getTool(AC);
406 }
407}
408
409std::optional<std::string>
410clang::driver::toolchains::HLSLToolChain::parseTargetProfile(
411 StringRef TargetProfile) {
412 return tryParseProfile(Profile: TargetProfile);
413}
414
415DerivedArgList *
416HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
417 Action::OffloadKind DeviceOffloadKind) const {
418 DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
419
420 const OptTable &Opts = getDriver().getOpts();
421
422 if (Args.hasArg(Ids: options::OPT_dxc_col_major) &&
423 Args.hasArg(Ids: options::OPT_dxc_row_major))
424 getDriver().Diag(DiagID: diag::err_drv_dxc_invalid_matrix_layout);
425
426 for (Arg *A : Args) {
427 if (A->getOption().getID() == options::OPT_dxc_all_resources_bound) {
428 DAL->AddFlagArg(BaseArg: nullptr,
429 Opt: Opts.getOption(Opt: options::OPT_hlsl_all_resources_bound));
430 A->claim();
431 continue;
432 }
433 if (A->getOption().getID() == options::OPT_dxil_validator_version) {
434 StringRef ValVerStr = A->getValue();
435 if (!isLegalValidatorVersion(ValVersionStr: ValVerStr, D: getDriver()))
436 continue;
437 }
438 if (A->getOption().getID() == options::OPT_dxc_entrypoint) {
439 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_hlsl_entrypoint),
440 Value: A->getValue());
441 A->claim();
442 continue;
443 }
444 if (A->getOption().getID() == options::OPT_dxc_rootsig_ver) {
445 DAL->AddJoinedArg(BaseArg: nullptr,
446 Opt: Opts.getOption(Opt: options::OPT_fdx_rootsignature_version),
447 Value: A->getValue());
448 A->claim();
449 continue;
450 }
451 if (A->getOption().getID() == options::OPT_dxc_rootsig_define) {
452 DAL->AddJoinedArg(BaseArg: nullptr,
453 Opt: Opts.getOption(Opt: options::OPT_fdx_rootsignature_define),
454 Value: A->getValue());
455 A->claim();
456 continue;
457 }
458 if (A->getOption().getID() == options::OPT__SLASH_O) {
459 StringRef OStr = A->getValue();
460 if (OStr == "d") {
461 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_O0));
462 A->claim();
463 continue;
464 } else {
465 DAL->AddJoinedArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_O), Value: OStr);
466 A->claim();
467 continue;
468 }
469 }
470 if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
471 // Translate -fcgl into -emit-llvm and -disable-llvm-passes.
472 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_emit_llvm));
473 DAL->AddFlagArg(BaseArg: nullptr,
474 Opt: Opts.getOption(Opt: options::OPT_disable_llvm_passes));
475 A->claim();
476 continue;
477 }
478 if (A->getOption().getID() == options::OPT_dxc_hlsl_version) {
479 // Translate -HV into -std for llvm
480 // depending on the value given
481 LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(Name: A->getValue());
482 if (LangStd != LangStandard::lang_unspecified) {
483 LangStandard l = LangStandard::getLangStandardForKind(K: LangStd);
484 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_std_EQ),
485 Value: l.getName());
486 } else {
487 getDriver().Diag(DiagID: diag::err_drv_invalid_value) << "HV" << A->getValue();
488 }
489
490 A->claim();
491 continue;
492 }
493 if (A->getOption().getID() == options::OPT_dxc_gis) {
494 // Translate -Gis into -ffp_model_EQ=strict
495 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_ffp_model_EQ),
496 Value: "strict");
497 A->claim();
498 continue;
499 }
500 if (A->getOption().getID() == options::OPT_fvk_use_dx_layout) {
501 // This is the only implemented layout so far.
502 A->claim();
503 continue;
504 }
505
506 if (A->getOption().getID() == options::OPT_fvk_use_scalar_layout) {
507 getDriver().Diag(DiagID: diag::err_drv_clang_unsupported) << A->getAsString(Args);
508 A->claim();
509 continue;
510 }
511
512 if (A->getOption().getID() == options::OPT_fvk_use_gl_layout) {
513 getDriver().Diag(DiagID: diag::err_drv_clang_unsupported) << A->getAsString(Args);
514 A->claim();
515 continue;
516 }
517
518 if (A->getOption().getID() == options::OPT_enable_16bit_types) {
519 // Translate -enable-16bit-types into -fnative-half-type and
520 // -fnative-int16-type
521 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_fnative_half_type));
522 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_fnative_int16_type));
523 A->claim();
524 continue;
525 }
526 if (A->getOption().getID() == options::OPT_dxc_col_major) {
527 DAL->AddJoinedArg(BaseArg: nullptr,
528 Opt: Opts.getOption(Opt: options::OPT_fmatrix_memory_layout_EQ),
529 Value: "column-major");
530 A->claim();
531 continue;
532 }
533 if (A->getOption().getID() == options::OPT_dxc_row_major) {
534 DAL->AddJoinedArg(BaseArg: nullptr,
535 Opt: Opts.getOption(Opt: options::OPT_fmatrix_memory_layout_EQ),
536 Value: "row-major");
537 A->claim();
538 continue;
539 }
540
541 // This is a temporary check until we support reflection generation for
542 // other targets.
543 if (A->getOption().getID() == options::OPT_dxc_Fre) {
544 if (Args.hasArg(Ids: options::OPT_metal)) {
545 if (!Args.hasArg(Ids: options::OPT_dxc_Fo))
546 getDriver().Diag(DiagID: diag::err_drv_dxc_Fre_requires_Fo_metal);
547 } else
548 getDriver().Diag(DiagID: diag::err_drv_unsupported_opt_for_target)
549 << "-Fre" << getTriple().getArchName();
550 A->claim();
551 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_dxc_Fre),
552 Value: A->getValue());
553 continue;
554 }
555
556 DAL->append(A);
557 }
558
559 if (getArch() == llvm::Triple::spirv) {
560 std::vector<std::string> SpvExtensionArgs =
561 Args.getAllArgValues(Id: options::OPT_fspv_extension_EQ);
562 if (checkExtensionArgsAreValid(SpvExtensionArgs, Driver: getDriver())) {
563 SmallString<1024> LlvmOption = getSpirvExtArg(SpvExtensionArgs);
564 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_mllvm),
565 Value: LlvmOption);
566 }
567 Args.claimAllArgs(Ids: options::OPT_fspv_extension_EQ);
568 }
569
570 if (!DAL->hasArg(Ids: options::OPT_O_Group)) {
571 DAL->AddJoinedArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_O), Value: "3");
572 }
573
574 return DAL;
575}
576
577bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const {
578 if (!Args.hasArg(Ids: options::OPT_dxc_Fo))
579 return false;
580
581 if (Args.getLastArg(Ids: options::OPT_dxc_disable_validation))
582 return false;
583
584 std::string DxvPath = GetProgramPath(Name: "dxv");
585 if (DxvPath != "dxv")
586 return true;
587
588 getDriver().Diag(DiagID: diag::warn_drv_dxc_missing_dxv);
589 return false;
590}
591
592bool HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const {
593 return Args.hasArg(Ids: options::OPT_metal) && Args.hasArg(Ids: options::OPT_dxc_Fo);
594}
595
596bool HLSLToolChain::requiresObjcopy(DerivedArgList &Args) const {
597 return Args.hasArg(Ids: options::OPT_dxc_Fo) &&
598 (Args.hasArg(Ids: options::OPT_dxc_strip_rootsignature) ||
599 Args.hasArg(Ids: options::OPT_dxc_Frs) || isRootSignatureTarget(Args));
600}
601
602bool HLSLToolChain::isLastJob(DerivedArgList &Args,
603 Action::ActionClass AC) const {
604 // Note: we check in the reverse order of execution
605 if (requiresBinaryTranslation(Args))
606 return AC == Action::Action::BinaryTranslatorJobClass;
607 if (requiresValidation(Args))
608 return AC == Action::Action::BinaryAnalyzeJobClass;
609 if (requiresObjcopy(Args))
610 return AC == Action::Action::ObjcopyJobClass;
611
612 // No translation, validation, or objcopy are required, so this action must
613 // output to the result file.
614 return true;
615}
616
617void HLSLToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {
618 CC1Args.push_back(Elt: "-Wconversion");
619 CC1Args.push_back(Elt: "-Wvector-conversion");
620 CC1Args.push_back(Elt: "-Wmatrix-conversion");
621}
622