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 "CommonArgs.h"
11#include "clang/Driver/Compilation.h"
12#include "clang/Driver/DriverDiagnostic.h"
13#include "clang/Driver/Job.h"
14#include "llvm/ADT/StringSwitch.h"
15#include "llvm/TargetParser/Triple.h"
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 // dxil-unknown-shadermodel-hull
102 llvm::Triple T;
103 T.setArch(Kind: Triple::ArchType::dxil);
104 T.setOSName(Triple::getOSTypeName(Kind: Triple::OSType::ShaderModel).str() +
105 VersionTuple(Major, Minor).getAsString());
106 T.setEnvironment(Kind);
107 if (isLegalShaderModel(T))
108 return T.getTriple();
109 else
110 return std::nullopt;
111}
112
113bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) {
114 VersionTuple Version;
115 if (Version.tryParse(string: ValVersionStr) || Version.getBuild() ||
116 Version.getSubminor() || !Version.getMinor()) {
117 D.Diag(diag::DiagID: err_drv_invalid_format_dxil_validator_version)
118 << ValVersionStr;
119 return false;
120 }
121
122 uint64_t Major = Version.getMajor();
123 uint64_t Minor = *Version.getMinor();
124 if (Major == 0 && Minor != 0) {
125 D.Diag(diag::DiagID: err_drv_invalid_empty_dxil_validator_version) << ValVersionStr;
126 return false;
127 }
128 VersionTuple MinVer(1, 0);
129 if (Version < MinVer) {
130 D.Diag(diag::DiagID: err_drv_invalid_range_dxil_validator_version) << ValVersionStr;
131 return false;
132 }
133 return true;
134}
135
136} // namespace
137
138void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
139 const InputInfo &Output,
140 const InputInfoList &Inputs,
141 const ArgList &Args,
142 const char *LinkingOutput) const {
143 std::string DxvPath = getToolChain().GetProgramPath(Name: "dxv");
144 assert(DxvPath != "dxv" && "cannot find dxv");
145
146 ArgStringList CmdArgs;
147 assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
148 const InputInfo &Input = Inputs[0];
149 assert(Input.isFilename() && "Unexpected verify input");
150 // Grabbing the output of the earlier cc1 run.
151 CmdArgs.push_back(Elt: Input.getFilename());
152 // Use the same name as output.
153 CmdArgs.push_back(Elt: "-o");
154 CmdArgs.push_back(Elt: Input.getFilename());
155
156 const char *Exec = Args.MakeArgString(Str: DxvPath);
157 C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(),
158 args&: Exec, args&: CmdArgs, args: Inputs, args: Input));
159}
160
161/// DirectX Toolchain
162HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
163 const ArgList &Args)
164 : ToolChain(D, Triple, Args) {
165 if (Args.hasArg(options::OPT_dxc_validator_path_EQ))
166 getProgramPaths().push_back(
167 Args.getLastArgValue(options::Id: OPT_dxc_validator_path_EQ).str());
168}
169
170Tool *clang::driver::toolchains::HLSLToolChain::getTool(
171 Action::ActionClass AC) const {
172 switch (AC) {
173 case Action::BinaryAnalyzeJobClass:
174 if (!Validator)
175 Validator.reset(p: new tools::hlsl::Validator(*this));
176 return Validator.get();
177 default:
178 return ToolChain::getTool(AC);
179 }
180}
181
182std::optional<std::string>
183clang::driver::toolchains::HLSLToolChain::parseTargetProfile(
184 StringRef TargetProfile) {
185 return tryParseProfile(Profile: TargetProfile);
186}
187
188DerivedArgList *
189HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
190 Action::OffloadKind DeviceOffloadKind) const {
191 DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
192
193 const OptTable &Opts = getDriver().getOpts();
194
195 for (Arg *A : Args) {
196 if (A->getOption().getID() == options::OPT_dxil_validator_version) {
197 StringRef ValVerStr = A->getValue();
198 std::string ErrorMsg;
199 if (!isLegalValidatorVersion(ValVersionStr: ValVerStr, D: getDriver()))
200 continue;
201 }
202 if (A->getOption().getID() == options::OPT_dxc_entrypoint) {
203 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_hlsl_entrypoint),
204 Value: A->getValue());
205 A->claim();
206 continue;
207 }
208 if (A->getOption().getID() == options::OPT__SLASH_O) {
209 StringRef OStr = A->getValue();
210 if (OStr == "d") {
211 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_O0));
212 A->claim();
213 continue;
214 } else {
215 DAL->AddJoinedArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_O), Value: OStr);
216 A->claim();
217 continue;
218 }
219 }
220 if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
221 // Translate fcgl into -S -emit-llvm and -disable-llvm-passes.
222 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_S));
223 DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_emit_llvm));
224 DAL->AddFlagArg(BaseArg: nullptr,
225 Opt: Opts.getOption(options::Opt: OPT_disable_llvm_passes));
226 A->claim();
227 continue;
228 }
229 if (A->getOption().getID() == options::OPT_dxc_hlsl_version) {
230 // Translate -HV into -std for llvm
231 // depending on the value given
232 LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(Name: A->getValue());
233 if (LangStd != LangStandard::lang_unspecified) {
234 LangStandard l = LangStandard::getLangStandardForKind(K: LangStd);
235 DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_std_EQ),
236 Value: l.getName());
237 } else {
238 getDriver().Diag(diag::DiagID: err_drv_invalid_value) << "HV" << A->getValue();
239 }
240
241 A->claim();
242 continue;
243 }
244 DAL->append(A);
245 }
246
247 // Add default validator version if not set.
248 // TODO: remove this once read validator version from validator.
249 if (!DAL->hasArg(options::OPT_dxil_validator_version)) {
250 const StringRef DefaultValidatorVer = "1.7";
251 DAL->AddSeparateArg(BaseArg: nullptr,
252 Opt: Opts.getOption(options::Opt: OPT_dxil_validator_version),
253 Value: DefaultValidatorVer);
254 }
255 if (!DAL->hasArg(options::OPT_O_Group)) {
256 DAL->AddJoinedArg(BaseArg: nullptr, Opt: Opts.getOption(options::Opt: OPT_O), Value: "3");
257 }
258
259 return DAL;
260}
261
262bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const {
263 if (Args.getLastArg(options::OPT_dxc_disable_validation))
264 return false;
265
266 std::string DxvPath = GetProgramPath(Name: "dxv");
267 if (DxvPath != "dxv")
268 return true;
269
270 getDriver().Diag(diag::DiagID: warn_drv_dxc_missing_dxv);
271 return false;
272}
273

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