clang 22.0.0git
HLSL.cpp
Go to the documentation of this file.
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"
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 }
66 return false;
67}
68
69std::optional<std::string> tryParseProfile(StringRef Profile) {
70 // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
72 Profile.split(Parts, "_");
73 if (Parts.size() != 3)
74 return std::nullopt;
75
76 Triple::EnvironmentType Kind =
77 StringSwitch<Triple::EnvironmentType>(Parts[0])
78 .Case("ps", Triple::EnvironmentType::Pixel)
79 .Case("vs", Triple::EnvironmentType::Vertex)
80 .Case("gs", Triple::EnvironmentType::Geometry)
81 .Case("hs", Triple::EnvironmentType::Hull)
82 .Case("ds", Triple::EnvironmentType::Domain)
83 .Case("cs", Triple::EnvironmentType::Compute)
84 .Case("lib", Triple::EnvironmentType::Library)
85 .Case("ms", Triple::EnvironmentType::Mesh)
86 .Case("as", Triple::EnvironmentType::Amplification)
87 .Default(Triple::EnvironmentType::UnknownEnvironment);
88 if (Kind == Triple::EnvironmentType::UnknownEnvironment)
89 return std::nullopt;
90
91 unsigned long long Major = 0;
92 if (llvm::getAsUnsignedInteger(Parts[1], 0, Major))
93 return std::nullopt;
94
95 unsigned long long Minor = 0;
96 if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library)
97 Minor = OfflineLibMinor;
98 else if (llvm::getAsUnsignedInteger(Parts[2], 0, Minor))
99 return std::nullopt;
100
101 // Determine DXIL version using the minor version number of Shader
102 // Model version specified in target profile. Prior to decoupling DXIL version
103 // numbering from that of Shader Model DXIL version 1.Y corresponds to SM 6.Y.
104 // E.g., dxilv1.Y-unknown-shadermodelX.Y-hull
105 llvm::Triple T;
106 Triple::SubArchType SubArch = llvm::Triple::NoSubArch;
107 switch (Minor) {
108 case 0:
109 SubArch = llvm::Triple::DXILSubArch_v1_0;
110 break;
111 case 1:
112 SubArch = llvm::Triple::DXILSubArch_v1_1;
113 break;
114 case 2:
115 SubArch = llvm::Triple::DXILSubArch_v1_2;
116 break;
117 case 3:
118 SubArch = llvm::Triple::DXILSubArch_v1_3;
119 break;
120 case 4:
121 SubArch = llvm::Triple::DXILSubArch_v1_4;
122 break;
123 case 5:
124 SubArch = llvm::Triple::DXILSubArch_v1_5;
125 break;
126 case 6:
127 SubArch = llvm::Triple::DXILSubArch_v1_6;
128 break;
129 case 7:
130 SubArch = llvm::Triple::DXILSubArch_v1_7;
131 break;
132 case 8:
133 SubArch = llvm::Triple::DXILSubArch_v1_8;
134 break;
135 case OfflineLibMinor:
136 // Always consider minor version x as the latest supported DXIL version
137 SubArch = llvm::Triple::LatestDXILSubArch;
138 break;
139 default:
140 // No DXIL Version corresponding to specified Shader Model version found
141 return std::nullopt;
142 }
143 T.setArch(Triple::ArchType::dxil, SubArch);
144 T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() +
145 VersionTuple(Major, Minor).getAsString());
146 T.setEnvironment(Kind);
147 if (isLegalShaderModel(T))
148 return T.getTriple();
149 else
150 return std::nullopt;
151}
152
153bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) {
154 VersionTuple Version;
155 if (Version.tryParse(ValVersionStr) || Version.getBuild() ||
156 Version.getSubminor() || !Version.getMinor()) {
157 D.Diag(diag::err_drv_invalid_format_dxil_validator_version)
158 << ValVersionStr;
159 return false;
160 }
161
162 uint64_t Major = Version.getMajor();
163 uint64_t Minor = *Version.getMinor();
164 if (Major == 0 && Minor != 0) {
165 D.Diag(diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr;
166 return false;
167 }
168 VersionTuple MinVer(1, 0);
169 if (Version < MinVer) {
170 D.Diag(diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr;
171 return false;
172 }
173 return true;
174}
175
176void getSpirvExtOperand(StringRef SpvExtensionArg, raw_ostream &out) {
177 // The extensions that are commented out are supported in DXC, but the SPIR-V
178 // backend does not know about them yet.
179 static const std::vector<StringRef> DxcSupportedExtensions = {
180 "SPV_KHR_16bit_storage", "SPV_KHR_device_group",
181 "SPV_KHR_fragment_shading_rate", "SPV_KHR_multiview",
182 "SPV_KHR_post_depth_coverage", "SPV_KHR_non_semantic_info",
183 "SPV_KHR_shader_draw_parameters", "SPV_KHR_ray_tracing",
184 "SPV_KHR_shader_clock", "SPV_EXT_demote_to_helper_invocation",
185 "SPV_EXT_descriptor_indexing", "SPV_EXT_fragment_fully_covered",
186 "SPV_EXT_fragment_invocation_density",
187 "SPV_EXT_fragment_shader_interlock", "SPV_EXT_mesh_shader",
188 "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer",
189 // "SPV_AMD_shader_early_and_late_fragment_tests",
190 "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type",
191 "SPV_KHR_ray_query", "SPV_EXT_shader_image_int64",
192 "SPV_KHR_fragment_shader_barycentric", "SPV_KHR_physical_storage_buffer",
193 "SPV_KHR_vulkan_memory_model",
194 // "SPV_KHR_compute_shader_derivatives",
195 // "SPV_KHR_maximal_reconvergence",
196 "SPV_KHR_float_controls", "SPV_NV_shader_subgroup_partitioned",
197 // "SPV_KHR_quad_control"
198 };
199
200 if (SpvExtensionArg.starts_with("SPV_")) {
201 out << "+" << SpvExtensionArg;
202 return;
203 }
204
205 if (SpvExtensionArg.compare_insensitive("DXC") == 0) {
206 bool first = true;
207 std::string Operand;
208 for (StringRef E : DxcSupportedExtensions) {
209 if (!first)
210 out << ",";
211 else
212 first = false;
213 out << "+" << E;
214 }
215 return;
216 }
217 out << SpvExtensionArg;
218}
219
220SmallString<1024> getSpirvExtArg(ArrayRef<std::string> SpvExtensionArgs) {
221 if (SpvExtensionArgs.empty()) {
222 return StringRef("-spirv-ext=all");
223 }
224
225 llvm::SmallString<1024> LlvmOption;
226 raw_svector_ostream out(LlvmOption);
227
228 out << "-spirv-ext=";
229 getSpirvExtOperand(SpvExtensionArgs[0], out);
230
231 SpvExtensionArgs = SpvExtensionArgs.slice(1);
232 for (StringRef Extension : SpvExtensionArgs) {
233 out << ",";
234 getSpirvExtOperand(Extension, out);
235 }
236 return LlvmOption;
237}
238
239bool isValidSPIRVExtensionName(const std::string &str) {
240 std::regex pattern("dxc|DXC|khr|KHR|SPV_[a-zA-Z0-9_]+");
241 return std::regex_match(str, pattern);
242}
243
244// SPIRV extension names are of the form `SPV_[a-zA-Z0-9_]+`. We want to
245// disallow obviously invalid names to avoid issues when parsing `spirv-ext`.
246bool checkExtensionArgsAreValid(ArrayRef<std::string> SpvExtensionArgs,
247 const Driver &Driver) {
248 bool AllValid = true;
249 for (auto Extension : SpvExtensionArgs) {
250 if (!isValidSPIRVExtensionName(Extension)) {
251 Driver.Diag(diag::err_drv_invalid_value)
252 << "-fspv-extension" << Extension;
253 AllValid = false;
254 }
255 }
256 return AllValid;
257}
258} // namespace
259
261 const InputInfo &Output,
262 const InputInfoList &Inputs,
263 const ArgList &Args,
264 const char *LinkingOutput) const {
265 std::string DxvPath = getToolChain().GetProgramPath("dxv");
266 assert(DxvPath != "dxv" && "cannot find dxv");
267
268 ArgStringList CmdArgs;
269 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
270 const InputInfo &Input = Inputs[0];
271 CmdArgs.push_back(Input.getFilename());
272 CmdArgs.push_back("-o");
273 CmdArgs.push_back(Output.getFilename());
274
275 const char *Exec = Args.MakeArgString(DxvPath);
276 C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
277 Exec, CmdArgs, Inputs, Input));
278}
279
281 Compilation &C, const JobAction &JA, const InputInfo &Output,
282 const InputInfoList &Inputs, const ArgList &Args,
283 const char *LinkingOutput) const {
284 std::string MSCPath = getToolChain().GetProgramPath("metal-shaderconverter");
285 ArgStringList CmdArgs;
286 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
287 const InputInfo &Input = Inputs[0];
288 CmdArgs.push_back(Input.getFilename());
289 CmdArgs.push_back("-o");
290 CmdArgs.push_back(Output.getFilename());
291
292 const char *Exec = Args.MakeArgString(MSCPath);
293 C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
294 Exec, CmdArgs, Inputs, Input));
295}
296
298 const InputInfo &Output,
299 const InputInfoList &Inputs,
300 const ArgList &Args,
301 const char *LinkingOutput) const {
302
303 std::string ObjcopyPath = getToolChain().GetProgramPath("llvm-objcopy");
304 const char *Exec = Args.MakeArgString(ObjcopyPath);
305
306 ArgStringList CmdArgs;
307 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
308 const InputInfo &Input = Inputs[0];
309 CmdArgs.push_back(Input.getFilename());
310 CmdArgs.push_back(Output.getFilename());
311
312 if (Args.hasArg(options::OPT_dxc_strip_rootsignature)) {
313 const char *Frs = Args.MakeArgString("--remove-section=RTS0");
314 CmdArgs.push_back(Frs);
315 }
316
317 assert(CmdArgs.size() > 2 && "Unnecessary invocation of objcopy.");
318
319 C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
320 Exec, CmdArgs, Inputs, Input));
321}
322
323/// DirectX Toolchain
324HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
325 const ArgList &Args)
326 : ToolChain(D, Triple, Args) {
327 if (Args.hasArg(options::OPT_dxc_validator_path_EQ))
328 getProgramPaths().push_back(
329 Args.getLastArgValue(options::OPT_dxc_validator_path_EQ).str());
330}
331
333 Action::ActionClass AC) const {
334 switch (AC) {
336 if (!Validator)
337 Validator.reset(new tools::hlsl::Validator(*this));
338 return Validator.get();
340 if (!MetalConverter)
341 MetalConverter.reset(new tools::hlsl::MetalConverter(*this));
342 return MetalConverter.get();
344 if (!LLVMObjcopy)
345 LLVMObjcopy.reset(new tools::hlsl::LLVMObjcopy(*this));
346 return LLVMObjcopy.get();
347 default:
348 return ToolChain::getTool(AC);
349 }
350}
351
352std::optional<std::string>
354 StringRef TargetProfile) {
355 return tryParseProfile(TargetProfile);
356}
357
358DerivedArgList *
359HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
360 Action::OffloadKind DeviceOffloadKind) const {
361 DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
362
363 const OptTable &Opts = getDriver().getOpts();
364
365 for (Arg *A : Args) {
366 if (A->getOption().getID() == options::OPT_dxil_validator_version) {
367 StringRef ValVerStr = A->getValue();
368 if (!isLegalValidatorVersion(ValVerStr, getDriver()))
369 continue;
370 }
371 if (A->getOption().getID() == options::OPT_dxc_entrypoint) {
372 DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_hlsl_entrypoint),
373 A->getValue());
374 A->claim();
375 continue;
376 }
377 if (A->getOption().getID() == options::OPT_dxc_rootsig_ver) {
378 DAL->AddJoinedArg(nullptr,
379 Opts.getOption(options::OPT_fdx_rootsignature_version),
380 A->getValue());
381 A->claim();
382 continue;
383 }
384 if (A->getOption().getID() == options::OPT_dxc_rootsig_define) {
385 DAL->AddJoinedArg(nullptr,
386 Opts.getOption(options::OPT_fdx_rootsignature_define),
387 A->getValue());
388 A->claim();
389 continue;
390 }
391 if (A->getOption().getID() == options::OPT__SLASH_O) {
392 StringRef OStr = A->getValue();
393 if (OStr == "d") {
394 DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_O0));
395 A->claim();
396 continue;
397 } else {
398 DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), OStr);
399 A->claim();
400 continue;
401 }
402 }
403 if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
404 // Translate -fcgl into -emit-llvm and -disable-llvm-passes.
405 DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm));
406 DAL->AddFlagArg(nullptr,
407 Opts.getOption(options::OPT_disable_llvm_passes));
408 A->claim();
409 continue;
410 }
411 if (A->getOption().getID() == options::OPT_dxc_hlsl_version) {
412 // Translate -HV into -std for llvm
413 // depending on the value given
414 LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(A->getValue());
415 if (LangStd != LangStandard::lang_unspecified) {
417 DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_std_EQ),
418 l.getName());
419 } else {
420 getDriver().Diag(diag::err_drv_invalid_value) << "HV" << A->getValue();
421 }
422
423 A->claim();
424 continue;
425 }
426 if (A->getOption().getID() == options::OPT_dxc_gis) {
427 // Translate -Gis into -ffp_model_EQ=strict
428 DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_ffp_model_EQ),
429 "strict");
430 A->claim();
431 continue;
432 }
433 if (A->getOption().getID() == options::OPT_fvk_use_dx_layout) {
434 // This is the only implemented layout so far.
435 A->claim();
436 continue;
437 }
438
439 if (A->getOption().getID() == options::OPT_fvk_use_scalar_layout) {
440 getDriver().Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args);
441 A->claim();
442 continue;
443 }
444
445 if (A->getOption().getID() == options::OPT_fvk_use_gl_layout) {
446 getDriver().Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args);
447 A->claim();
448 continue;
449 }
450
451 DAL->append(A);
452 }
453
454 if (getArch() == llvm::Triple::spirv) {
455 std::vector<std::string> SpvExtensionArgs =
456 Args.getAllArgValues(options::OPT_fspv_extension_EQ);
457 if (checkExtensionArgsAreValid(SpvExtensionArgs, getDriver())) {
458 SmallString<1024> LlvmOption = getSpirvExtArg(SpvExtensionArgs);
459 DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_mllvm),
460 LlvmOption);
461 }
462 Args.claimAllArgs(options::OPT_fspv_extension_EQ);
463 }
464
465 if (!DAL->hasArg(options::OPT_O_Group)) {
466 DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), "3");
467 }
468
469 return DAL;
470}
471
472bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const {
473 if (!Args.hasArg(options::OPT_dxc_Fo))
474 return false;
475
476 if (Args.getLastArg(options::OPT_dxc_disable_validation))
477 return false;
478
479 std::string DxvPath = GetProgramPath("dxv");
480 if (DxvPath != "dxv")
481 return true;
482
483 getDriver().Diag(diag::warn_drv_dxc_missing_dxv);
484 return false;
485}
486
487bool HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const {
488 return Args.hasArg(options::OPT_metal) && Args.hasArg(options::OPT_dxc_Fo);
489}
490
491bool HLSLToolChain::requiresObjcopy(DerivedArgList &Args) const {
492 return Args.hasArg(options::OPT_dxc_Fo) &&
493 Args.hasArg(options::OPT_dxc_strip_rootsignature);
494}
495
496bool HLSLToolChain::isLastJob(DerivedArgList &Args,
497 Action::ActionClass AC) const {
498 // Note: we check in the reverse order of execution
500 return AC == Action::Action::BinaryTranslatorJobClass;
501 if (requiresValidation(Args))
502 return AC == Action::Action::BinaryAnalyzeJobClass;
503 if (requiresObjcopy(Args))
504 return AC == Action::Action::ObjcopyJobClass;
505
506 // No translation, validation, or objcopy are required, so this action must
507 // output to the result file.
508 return true;
509}
const Decl * D
Expr * E
Compilation - A set of tasks to perform for a single driver invocation.
Definition: Compilation.h:45
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
Definition: Driver.h:99
DiagnosticBuilder Diag(unsigned DiagID) const
Definition: Driver.h:169
const llvm::opt::OptTable & getOpts() const
Definition: Driver.h:428
InputInfo - Wrapper for information about an input source.
Definition: InputInfo.h:22
const char * getFilename() const
Definition: InputInfo.h:83
ToolChain - Access to tools for a single platform.
Definition: ToolChain.h:92
llvm::Triple::ArchType getArch() const
Definition: ToolChain.h:269
const Driver & getDriver() const
Definition: ToolChain.h:253
path_list & getProgramPaths()
Definition: ToolChain.h:298
std::string GetProgramPath(const char *Name) const
Definition: ToolChain.cpp:1094
virtual Tool * getTool(Action::ActionClass AC) const
Definition: ToolChain.cpp:633
Tool - Information on a specific compilation tool.
Definition: Tool.h:32
const ToolChain & getToolChain() const
Definition: Tool.h:52
bool requiresBinaryTranslation(llvm::opt::DerivedArgList &Args) const
Definition: HLSL.cpp:487
bool isLastJob(llvm::opt::DerivedArgList &Args, Action::ActionClass AC) const
If we are targeting DXIL then the last job should output the DXContainer to the specified output file...
Definition: HLSL.cpp:496
Tool * getTool(Action::ActionClass AC) const override
Definition: HLSL.cpp:332
HLSLToolChain(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args)
DirectX Toolchain.
Definition: HLSL.cpp:324
bool requiresObjcopy(llvm::opt::DerivedArgList &Args) const
Definition: HLSL.cpp:491
llvm::opt::DerivedArgList * TranslateArgs(const llvm::opt::DerivedArgList &Args, StringRef BoundArch, Action::OffloadKind DeviceOffloadKind) const override
TranslateArgs - Create a new derived argument list for any argument translations this ToolChain may w...
Definition: HLSL.cpp:359
bool requiresValidation(llvm::opt::DerivedArgList &Args) const
Definition: HLSL.cpp:472
static std::optional< std::string > parseTargetProfile(StringRef TargetProfile)
Definition: HLSL.cpp:353
void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override
ConstructJob - Construct jobs to perform the action JA, writing to Output and with Inputs,...
Definition: HLSL.cpp:297
void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override
ConstructJob - Construct jobs to perform the action JA, writing to Output and with Inputs,...
Definition: HLSL.cpp:280
void ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override
ConstructJob - Construct jobs to perform the action JA, writing to Output and with Inputs,...
Definition: HLSL.cpp:260
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
llvm::StringRef getAsString(SyncScope S)
Definition: SyncScope.h:60
unsigned long uint64_t
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
LangStandard - Information about the properties of a particular language standard.
Definition: LangStandard.h:71
static Kind getHLSLLangKind(StringRef Name)
static const LangStandard & getLangStandardForKind(Kind K)
const char * getName() const
getName - Get the name of this standard.
Definition: LangStandard.h:86
static constexpr ResponseFileSupport None()
Returns a ResponseFileSupport indicating that response files are not supported.
Definition: Job.h:78