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 }
66 return false;
67}
68
69std::optional<std::string> tryParseProfile(StringRef Profile) {
70 // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
71 SmallVector<StringRef, 3> Parts;
72 Profile.split(A&: Parts, Separator: "_");
73 if (Parts.size() != 3)
74 return std::nullopt;
75
76 Triple::EnvironmentType Kind =
77 StringSwitch<Triple::EnvironmentType>(Parts[0])
78 .Case(S: "ps", Value: Triple::EnvironmentType::Pixel)
79 .Case(S: "vs", Value: Triple::EnvironmentType::Vertex)
80 .Case(S: "gs", Value: Triple::EnvironmentType::Geometry)
81 .Case(S: "hs", Value: Triple::EnvironmentType::Hull)
82 .Case(S: "ds", Value: Triple::EnvironmentType::Domain)
83 .Case(S: "cs", Value: Triple::EnvironmentType::Compute)
84 .Case(S: "lib", Value: Triple::EnvironmentType::Library)
85 .Case(S: "ms", Value: Triple::EnvironmentType::Mesh)
86 .Case(S: "as", Value: Triple::EnvironmentType::Amplification)
87 .Default(Value: Triple::EnvironmentType::UnknownEnvironment);
88 if (Kind == Triple::EnvironmentType::UnknownEnvironment)
89 return std::nullopt;
90
91 unsigned long long Major = 0;
92 if (llvm::getAsUnsignedInteger(Str: Parts[1], Radix: 0, Result&: 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(Str: Parts[2], Radix: 0, Result&: 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(Kind: Triple::ArchType::dxil, SubArch);
144 T.setOSName(Triple::getOSTypeName(Kind: 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(string: ValVersionStr) || Version.getBuild() ||
156 Version.getSubminor() || !Version.getMinor()) {
157 D.Diag(diag::DiagID: 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::DiagID: 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::DiagID: err_drv_invalid_range_dxil_validator_version) << ValVersionStr;
171 return false;
172 }
173 return true;
174}
175
176std::string getSpirvExtArg(ArrayRef<std::string> SpvExtensionArgs) {
177 if (SpvExtensionArgs.empty()) {
178 return "-spirv-ext=all";
179 }
180
181 std::string LlvmOption =
182 (Twine("-spirv-ext=+") + SpvExtensionArgs.front()).str();
183 SpvExtensionArgs = SpvExtensionArgs.slice(N: 1);
184 for (auto Extension : SpvExtensionArgs) {
185 LlvmOption = (Twine(LlvmOption) + ",+" + Extension).str();
186 }
187 return LlvmOption;
188}
189
190bool isValidSPIRVExtensionName(const std::string &str) {
191 std::regex pattern("SPV_[a-zA-Z0-9_]+");
192 return std::regex_match(s: str, re: pattern);
193}
194
195// SPIRV extension names are of the form `SPV_[a-zA-Z0-9_]+`. We want to
196// disallow obviously invalid names to avoid issues when parsing `spirv-ext`.
197bool checkExtensionArgsAreValid(ArrayRef<std::string> SpvExtensionArgs,
198 const Driver &Driver) {
199 bool AllValid = true;
200 for (auto Extension : SpvExtensionArgs) {
201 if (!isValidSPIRVExtensionName(str: Extension)) {
202 Driver.Diag(diag::DiagID: err_drv_invalid_value)
203 << "-fspv_extension" << Extension;
204 AllValid = false;
205 }
206 }
207 return AllValid;
208}
209} // namespace
210
211void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
212 const InputInfo &Output,
213 const InputInfoList &Inputs,
214 const ArgList &Args,
215 const char *LinkingOutput) const {
216 std::string DxvPath = getToolChain().GetProgramPath(Name: "dxv");
217 assert(DxvPath != "dxv" && "cannot find dxv");
218
219 ArgStringList CmdArgs;
220 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
221 const InputInfo &Input = Inputs[0];
222 CmdArgs.push_back(Elt: Input.getFilename());
223 CmdArgs.push_back(Elt: "-o");
224 CmdArgs.push_back(Elt: Output.getFilename());
225
226 const char *Exec = Args.MakeArgString(Str: DxvPath);
227 C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(),
228 args&: Exec, args&: CmdArgs, args: Inputs, args: Input));
229}
230
231void tools::hlsl::MetalConverter::ConstructJob(
232 Compilation &C, const JobAction &JA, const InputInfo &Output,
233 const InputInfoList &Inputs, const ArgList &Args,
234 const char *LinkingOutput) const {
235 std::string MSCPath = getToolChain().GetProgramPath(Name: "metal-shaderconverter");
236 ArgStringList CmdArgs;
237 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
238 const InputInfo &Input = Inputs[0];
239 CmdArgs.push_back(Elt: Input.getFilename());
240 CmdArgs.push_back(Elt: "-o");
241 CmdArgs.push_back(Elt: Output.getFilename());
242
243 const char *Exec = Args.MakeArgString(Str: MSCPath);
244 C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(),
245 args&: Exec, args&: CmdArgs, args: Inputs, args: Input));
246}
247
248/// DirectX Toolchain
249HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
250 const ArgList &Args)
251 : ToolChain(D, Triple, Args) {
252 if (Args.hasArg(options::OPT_dxc_validator_path_EQ))
253 getProgramPaths().push_back(
254 Args.getLastArgValue(options::Id: OPT_dxc_validator_path_EQ).str());
255}
256
257Tool *clang::driver::toolchains::HLSLToolChain::getTool(
258 Action::ActionClass AC) const {
259 switch (AC) {
260 case Action::BinaryAnalyzeJobClass:
261 if (!Validator)
262 Validator.reset(p: new tools::hlsl::Validator(*this));
263 return Validator.get();
264 case Action::BinaryTranslatorJobClass:
265 if (!MetalConverter)
266 MetalConverter.reset(p: new tools::hlsl::MetalConverter(*this));
267 return MetalConverter.get();
268 default:
269 return ToolChain::getTool(AC);
270 }
271}
272
273std::optional<std::string>
274clang::driver::toolchains::HLSLToolChain::parseTargetProfile(
275 StringRef TargetProfile) {
276 return tryParseProfile(Profile: TargetProfile);
277}
278
279DerivedArgList *
280HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
281 Action::OffloadKind DeviceOffloadKind) const {
282 DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
283
284 const OptTable &Opts = getDriver().getOpts();
285
286 for (Arg *A : Args) {
287 if (A->getOption().getID() == options::OPT_dxil_validator_version) {
288 StringRef ValVerStr = A->getValue();
289 if (!isLegalValidatorVersion(ValVersionStr: ValVerStr, D: getDriver()))
290 continue;
291 }
292 if (A->getOption().getID() == options::OPT_dxc_entrypoint) {
293 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_hlsl_entrypoint),
294 Value: A->getValue());
295 A->claim();
296 continue;
297 }
298 if (A->getOption().getID() == options::OPT__SLASH_O) {
299 StringRef OStr = A->getValue();
300 if (OStr == "d") {
301 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_O0));
302 A->claim();
303 continue;
304 } else {
305 DAL->AddJoinedArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_O), Value: OStr);
306 A->claim();
307 continue;
308 }
309 }
310 if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
311 // Translate -fcgl into -emit-llvm and -disable-llvm-passes.
312 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_emit_llvm));
313 DAL->AddFlagArg(BaseArg: nullptr,
314 Opt: Opts.getOption(options::Opt: OPT_disable_llvm_passes));
315 A->claim();
316 continue;
317 }
318 if (A->getOption().getID() == options::OPT_dxc_hlsl_version) {
319 // Translate -HV into -std for llvm
320 // depending on the value given
321 LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(Name: A->getValue());
322 if (LangStd != LangStandard::lang_unspecified) {
323 LangStandard l = LangStandard::getLangStandardForKind(K: LangStd);
324 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_std_EQ),
325 Value: l.getName());
326 } else {
327 getDriver().Diag(diag::DiagID: err_drv_invalid_value) << "HV" << A->getValue();
328 }
329
330 A->claim();
331 continue;
332 }
333 DAL->append(A);
334 }
335
336 if (getArch() == llvm::Triple::spirv) {
337 std::vector<std::string> SpvExtensionArgs =
338 Args.getAllArgValues(options::Id: OPT_fspv_extension_EQ);
339 if (checkExtensionArgsAreValid(SpvExtensionArgs, Driver: getDriver())) {
340 std::string LlvmOption = getSpirvExtArg(SpvExtensionArgs);
341 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_mllvm),
342 Value: LlvmOption);
343 }
344 Args.claimAllArgs(options::OPT_fspv_extension_EQ);
345 }
346
347 if (!DAL->hasArg(options::OPT_O_Group)) {
348 DAL->AddJoinedArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_O), Value: "3");
349 }
350
351 return DAL;
352}
353
354bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const {
355 if (!Args.hasArg(options::OPT_dxc_Fo))
356 return false;
357
358 if (Args.getLastArg(options::OPT_dxc_disable_validation))
359 return false;
360
361 std::string DxvPath = GetProgramPath(Name: "dxv");
362 if (DxvPath != "dxv")
363 return true;
364
365 getDriver().Diag(diag::DiagID: warn_drv_dxc_missing_dxv);
366 return false;
367}
368
369bool HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const {
370 return Args.hasArg(options::OPT_metal) && Args.hasArg(options::OPT_dxc_Fo);
371}
372
373bool HLSLToolChain::isLastJob(DerivedArgList &Args,
374 Action::ActionClass AC) const {
375 bool HasTranslation = requiresBinaryTranslation(Args);
376 bool HasValidation = requiresValidation(Args);
377 // If translation and validation are not required, we should only have one
378 // action.
379 if (!HasTranslation && !HasValidation)
380 return true;
381 if ((HasTranslation && AC == Action::BinaryTranslatorJobClass) ||
382 (!HasTranslation && HasValidation && AC == Action::BinaryAnalyzeJobClass))
383 return true;
384 return false;
385}
386

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang/lib/Driver/ToolChains/HLSL.cpp