xref: /freebsd-src/contrib/llvm-project/clang/lib/Driver/ToolChains/HLSL.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
181ad6265SDimitry Andric //===--- HLSL.cpp - HLSL ToolChain Implementations --------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric 
981ad6265SDimitry Andric #include "HLSL.h"
1081ad6265SDimitry Andric #include "CommonArgs.h"
1106c3fb27SDimitry Andric #include "clang/Driver/Compilation.h"
1281ad6265SDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
1306c3fb27SDimitry Andric #include "clang/Driver/Job.h"
1481ad6265SDimitry Andric #include "llvm/ADT/StringSwitch.h"
1506c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h"
1681ad6265SDimitry Andric 
1781ad6265SDimitry Andric using namespace clang::driver;
1881ad6265SDimitry Andric using namespace clang::driver::tools;
1981ad6265SDimitry Andric using namespace clang::driver::toolchains;
2081ad6265SDimitry Andric using namespace clang;
2181ad6265SDimitry Andric using namespace llvm::opt;
2281ad6265SDimitry Andric using namespace llvm;
2381ad6265SDimitry Andric 
2481ad6265SDimitry Andric namespace {
2581ad6265SDimitry Andric 
2681ad6265SDimitry Andric const unsigned OfflineLibMinor = 0xF;
2781ad6265SDimitry Andric 
2881ad6265SDimitry Andric bool isLegalShaderModel(Triple &T) {
2981ad6265SDimitry Andric   if (T.getOS() != Triple::OSType::ShaderModel)
3081ad6265SDimitry Andric     return false;
3181ad6265SDimitry Andric 
3281ad6265SDimitry Andric   auto Version = T.getOSVersion();
3381ad6265SDimitry Andric   if (Version.getBuild())
3481ad6265SDimitry Andric     return false;
3581ad6265SDimitry Andric   if (Version.getSubminor())
3681ad6265SDimitry Andric     return false;
3781ad6265SDimitry Andric 
3881ad6265SDimitry Andric   auto Kind = T.getEnvironment();
3981ad6265SDimitry Andric 
4081ad6265SDimitry Andric   switch (Kind) {
4181ad6265SDimitry Andric   default:
4281ad6265SDimitry Andric     return false;
4381ad6265SDimitry Andric   case Triple::EnvironmentType::Vertex:
4481ad6265SDimitry Andric   case Triple::EnvironmentType::Hull:
4581ad6265SDimitry Andric   case Triple::EnvironmentType::Domain:
4681ad6265SDimitry Andric   case Triple::EnvironmentType::Geometry:
4781ad6265SDimitry Andric   case Triple::EnvironmentType::Pixel:
4881ad6265SDimitry Andric   case Triple::EnvironmentType::Compute: {
4981ad6265SDimitry Andric     VersionTuple MinVer(4, 0);
5081ad6265SDimitry Andric     return MinVer <= Version;
5181ad6265SDimitry Andric   } break;
5281ad6265SDimitry Andric   case Triple::EnvironmentType::Library: {
5381ad6265SDimitry Andric     VersionTuple SM6x(6, OfflineLibMinor);
5481ad6265SDimitry Andric     if (Version == SM6x)
5581ad6265SDimitry Andric       return true;
5681ad6265SDimitry Andric 
5781ad6265SDimitry Andric     VersionTuple MinVer(6, 3);
5881ad6265SDimitry Andric     return MinVer <= Version;
5981ad6265SDimitry Andric   } break;
6081ad6265SDimitry Andric   case Triple::EnvironmentType::Amplification:
6181ad6265SDimitry Andric   case Triple::EnvironmentType::Mesh: {
6281ad6265SDimitry Andric     VersionTuple MinVer(6, 5);
6381ad6265SDimitry Andric     return MinVer <= Version;
6481ad6265SDimitry Andric   } break;
6581ad6265SDimitry Andric   }
6681ad6265SDimitry Andric   return false;
6781ad6265SDimitry Andric }
6881ad6265SDimitry Andric 
69bdd1243dSDimitry Andric std::optional<std::string> tryParseProfile(StringRef Profile) {
7081ad6265SDimitry Andric   // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
7181ad6265SDimitry Andric   SmallVector<StringRef, 3> Parts;
7281ad6265SDimitry Andric   Profile.split(Parts, "_");
7381ad6265SDimitry Andric   if (Parts.size() != 3)
74bdd1243dSDimitry Andric     return std::nullopt;
7581ad6265SDimitry Andric 
7681ad6265SDimitry Andric   Triple::EnvironmentType Kind =
7781ad6265SDimitry Andric       StringSwitch<Triple::EnvironmentType>(Parts[0])
7881ad6265SDimitry Andric           .Case("ps", Triple::EnvironmentType::Pixel)
7981ad6265SDimitry Andric           .Case("vs", Triple::EnvironmentType::Vertex)
8081ad6265SDimitry Andric           .Case("gs", Triple::EnvironmentType::Geometry)
8181ad6265SDimitry Andric           .Case("hs", Triple::EnvironmentType::Hull)
8281ad6265SDimitry Andric           .Case("ds", Triple::EnvironmentType::Domain)
8381ad6265SDimitry Andric           .Case("cs", Triple::EnvironmentType::Compute)
8481ad6265SDimitry Andric           .Case("lib", Triple::EnvironmentType::Library)
8581ad6265SDimitry Andric           .Case("ms", Triple::EnvironmentType::Mesh)
8681ad6265SDimitry Andric           .Case("as", Triple::EnvironmentType::Amplification)
8781ad6265SDimitry Andric           .Default(Triple::EnvironmentType::UnknownEnvironment);
8881ad6265SDimitry Andric   if (Kind == Triple::EnvironmentType::UnknownEnvironment)
89bdd1243dSDimitry Andric     return std::nullopt;
9081ad6265SDimitry Andric 
9181ad6265SDimitry Andric   unsigned long long Major = 0;
9281ad6265SDimitry Andric   if (llvm::getAsUnsignedInteger(Parts[1], 0, Major))
93bdd1243dSDimitry Andric     return std::nullopt;
9481ad6265SDimitry Andric 
9581ad6265SDimitry Andric   unsigned long long Minor = 0;
9681ad6265SDimitry Andric   if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library)
9781ad6265SDimitry Andric     Minor = OfflineLibMinor;
9881ad6265SDimitry Andric   else if (llvm::getAsUnsignedInteger(Parts[2], 0, Minor))
99bdd1243dSDimitry Andric     return std::nullopt;
10081ad6265SDimitry Andric 
101*0fca6ea1SDimitry Andric   // Determine DXIL version using the minor version number of Shader
102*0fca6ea1SDimitry Andric   // Model version specified in target profile. Prior to decoupling DXIL version
103*0fca6ea1SDimitry Andric   // numbering from that of Shader Model DXIL version 1.Y corresponds to SM 6.Y.
104*0fca6ea1SDimitry Andric   // E.g., dxilv1.Y-unknown-shadermodelX.Y-hull
10581ad6265SDimitry Andric   llvm::Triple T;
106*0fca6ea1SDimitry Andric   Triple::SubArchType SubArch = llvm::Triple::NoSubArch;
107*0fca6ea1SDimitry Andric   switch (Minor) {
108*0fca6ea1SDimitry Andric   case 0:
109*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_0;
110*0fca6ea1SDimitry Andric     break;
111*0fca6ea1SDimitry Andric   case 1:
112*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_1;
113*0fca6ea1SDimitry Andric     break;
114*0fca6ea1SDimitry Andric   case 2:
115*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_2;
116*0fca6ea1SDimitry Andric     break;
117*0fca6ea1SDimitry Andric   case 3:
118*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_3;
119*0fca6ea1SDimitry Andric     break;
120*0fca6ea1SDimitry Andric   case 4:
121*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_4;
122*0fca6ea1SDimitry Andric     break;
123*0fca6ea1SDimitry Andric   case 5:
124*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_5;
125*0fca6ea1SDimitry Andric     break;
126*0fca6ea1SDimitry Andric   case 6:
127*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_6;
128*0fca6ea1SDimitry Andric     break;
129*0fca6ea1SDimitry Andric   case 7:
130*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_7;
131*0fca6ea1SDimitry Andric     break;
132*0fca6ea1SDimitry Andric   case 8:
133*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::DXILSubArch_v1_8;
134*0fca6ea1SDimitry Andric     break;
135*0fca6ea1SDimitry Andric   case OfflineLibMinor:
136*0fca6ea1SDimitry Andric     // Always consider minor version x as the latest supported DXIL version
137*0fca6ea1SDimitry Andric     SubArch = llvm::Triple::LatestDXILSubArch;
138*0fca6ea1SDimitry Andric     break;
139*0fca6ea1SDimitry Andric   default:
140*0fca6ea1SDimitry Andric     // No DXIL Version corresponding to specified Shader Model version found
141*0fca6ea1SDimitry Andric     return std::nullopt;
142*0fca6ea1SDimitry Andric   }
143*0fca6ea1SDimitry Andric   T.setArch(Triple::ArchType::dxil, SubArch);
14481ad6265SDimitry Andric   T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() +
14581ad6265SDimitry Andric               VersionTuple(Major, Minor).getAsString());
14681ad6265SDimitry Andric   T.setEnvironment(Kind);
14781ad6265SDimitry Andric   if (isLegalShaderModel(T))
14881ad6265SDimitry Andric     return T.getTriple();
14981ad6265SDimitry Andric   else
150bdd1243dSDimitry Andric     return std::nullopt;
15181ad6265SDimitry Andric }
15281ad6265SDimitry Andric 
15381ad6265SDimitry Andric bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) {
15481ad6265SDimitry Andric   VersionTuple Version;
15581ad6265SDimitry Andric   if (Version.tryParse(ValVersionStr) || Version.getBuild() ||
15681ad6265SDimitry Andric       Version.getSubminor() || !Version.getMinor()) {
15781ad6265SDimitry Andric     D.Diag(diag::err_drv_invalid_format_dxil_validator_version)
15881ad6265SDimitry Andric         << ValVersionStr;
15981ad6265SDimitry Andric     return false;
16081ad6265SDimitry Andric   }
16181ad6265SDimitry Andric 
16281ad6265SDimitry Andric   uint64_t Major = Version.getMajor();
16381ad6265SDimitry Andric   uint64_t Minor = *Version.getMinor();
16481ad6265SDimitry Andric   if (Major == 0 && Minor != 0) {
16581ad6265SDimitry Andric     D.Diag(diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr;
16681ad6265SDimitry Andric     return false;
16781ad6265SDimitry Andric   }
16881ad6265SDimitry Andric   VersionTuple MinVer(1, 0);
16981ad6265SDimitry Andric   if (Version < MinVer) {
17081ad6265SDimitry Andric     D.Diag(diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr;
17181ad6265SDimitry Andric     return false;
17281ad6265SDimitry Andric   }
17381ad6265SDimitry Andric   return true;
17481ad6265SDimitry Andric }
17581ad6265SDimitry Andric 
17681ad6265SDimitry Andric } // namespace
17781ad6265SDimitry Andric 
17806c3fb27SDimitry Andric void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
17906c3fb27SDimitry Andric                                           const InputInfo &Output,
18006c3fb27SDimitry Andric                                           const InputInfoList &Inputs,
18106c3fb27SDimitry Andric                                           const ArgList &Args,
18206c3fb27SDimitry Andric                                           const char *LinkingOutput) const {
18306c3fb27SDimitry Andric   std::string DxvPath = getToolChain().GetProgramPath("dxv");
18406c3fb27SDimitry Andric   assert(DxvPath != "dxv" && "cannot find dxv");
18506c3fb27SDimitry Andric 
18606c3fb27SDimitry Andric   ArgStringList CmdArgs;
18706c3fb27SDimitry Andric   assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
18806c3fb27SDimitry Andric   const InputInfo &Input = Inputs[0];
18906c3fb27SDimitry Andric   assert(Input.isFilename() && "Unexpected verify input");
19006c3fb27SDimitry Andric   // Grabbing the output of the earlier cc1 run.
19106c3fb27SDimitry Andric   CmdArgs.push_back(Input.getFilename());
19206c3fb27SDimitry Andric   // Use the same name as output.
19306c3fb27SDimitry Andric   CmdArgs.push_back("-o");
19406c3fb27SDimitry Andric   CmdArgs.push_back(Input.getFilename());
19506c3fb27SDimitry Andric 
19606c3fb27SDimitry Andric   const char *Exec = Args.MakeArgString(DxvPath);
19706c3fb27SDimitry Andric   C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
19806c3fb27SDimitry Andric                                          Exec, CmdArgs, Inputs, Input));
19906c3fb27SDimitry Andric }
20006c3fb27SDimitry Andric 
20181ad6265SDimitry Andric /// DirectX Toolchain
20281ad6265SDimitry Andric HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
20381ad6265SDimitry Andric                              const ArgList &Args)
20406c3fb27SDimitry Andric     : ToolChain(D, Triple, Args) {
20506c3fb27SDimitry Andric   if (Args.hasArg(options::OPT_dxc_validator_path_EQ))
20606c3fb27SDimitry Andric     getProgramPaths().push_back(
20706c3fb27SDimitry Andric         Args.getLastArgValue(options::OPT_dxc_validator_path_EQ).str());
20806c3fb27SDimitry Andric }
20906c3fb27SDimitry Andric 
21006c3fb27SDimitry Andric Tool *clang::driver::toolchains::HLSLToolChain::getTool(
21106c3fb27SDimitry Andric     Action::ActionClass AC) const {
21206c3fb27SDimitry Andric   switch (AC) {
21306c3fb27SDimitry Andric   case Action::BinaryAnalyzeJobClass:
21406c3fb27SDimitry Andric     if (!Validator)
21506c3fb27SDimitry Andric       Validator.reset(new tools::hlsl::Validator(*this));
21606c3fb27SDimitry Andric     return Validator.get();
21706c3fb27SDimitry Andric   default:
21806c3fb27SDimitry Andric     return ToolChain::getTool(AC);
21906c3fb27SDimitry Andric   }
22006c3fb27SDimitry Andric }
22181ad6265SDimitry Andric 
222bdd1243dSDimitry Andric std::optional<std::string>
22381ad6265SDimitry Andric clang::driver::toolchains::HLSLToolChain::parseTargetProfile(
22481ad6265SDimitry Andric     StringRef TargetProfile) {
22581ad6265SDimitry Andric   return tryParseProfile(TargetProfile);
22681ad6265SDimitry Andric }
22781ad6265SDimitry Andric 
22881ad6265SDimitry Andric DerivedArgList *
22981ad6265SDimitry Andric HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
23081ad6265SDimitry Andric                              Action::OffloadKind DeviceOffloadKind) const {
23181ad6265SDimitry Andric   DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
23281ad6265SDimitry Andric 
23381ad6265SDimitry Andric   const OptTable &Opts = getDriver().getOpts();
23481ad6265SDimitry Andric 
23581ad6265SDimitry Andric   for (Arg *A : Args) {
23681ad6265SDimitry Andric     if (A->getOption().getID() == options::OPT_dxil_validator_version) {
23781ad6265SDimitry Andric       StringRef ValVerStr = A->getValue();
23881ad6265SDimitry Andric       std::string ErrorMsg;
23981ad6265SDimitry Andric       if (!isLegalValidatorVersion(ValVerStr, getDriver()))
24081ad6265SDimitry Andric         continue;
24181ad6265SDimitry Andric     }
242bdd1243dSDimitry Andric     if (A->getOption().getID() == options::OPT_dxc_entrypoint) {
243bdd1243dSDimitry Andric       DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_hlsl_entrypoint),
244bdd1243dSDimitry Andric                           A->getValue());
245bdd1243dSDimitry Andric       A->claim();
246bdd1243dSDimitry Andric       continue;
247bdd1243dSDimitry Andric     }
248bdd1243dSDimitry Andric     if (A->getOption().getID() == options::OPT__SLASH_O) {
249bdd1243dSDimitry Andric       StringRef OStr = A->getValue();
250bdd1243dSDimitry Andric       if (OStr == "d") {
251bdd1243dSDimitry Andric         DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_O0));
252bdd1243dSDimitry Andric         A->claim();
253bdd1243dSDimitry Andric         continue;
254bdd1243dSDimitry Andric       } else {
255bdd1243dSDimitry Andric         DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), OStr);
256bdd1243dSDimitry Andric         A->claim();
257bdd1243dSDimitry Andric         continue;
258bdd1243dSDimitry Andric       }
259bdd1243dSDimitry Andric     }
26081ad6265SDimitry Andric     if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
261*0fca6ea1SDimitry Andric       // Translate -fcgl into -emit-llvm and -disable-llvm-passes.
26281ad6265SDimitry Andric       DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm));
26381ad6265SDimitry Andric       DAL->AddFlagArg(nullptr,
26481ad6265SDimitry Andric                       Opts.getOption(options::OPT_disable_llvm_passes));
26581ad6265SDimitry Andric       A->claim();
26681ad6265SDimitry Andric       continue;
26781ad6265SDimitry Andric     }
268*0fca6ea1SDimitry Andric     if (A->getOption().getID() == options::OPT_dxc_hlsl_version) {
269*0fca6ea1SDimitry Andric       // Translate -HV into -std for llvm
270*0fca6ea1SDimitry Andric       // depending on the value given
271*0fca6ea1SDimitry Andric       LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(A->getValue());
272*0fca6ea1SDimitry Andric       if (LangStd != LangStandard::lang_unspecified) {
273*0fca6ea1SDimitry Andric         LangStandard l = LangStandard::getLangStandardForKind(LangStd);
274*0fca6ea1SDimitry Andric         DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_std_EQ),
275*0fca6ea1SDimitry Andric                             l.getName());
276*0fca6ea1SDimitry Andric       } else {
277*0fca6ea1SDimitry Andric         getDriver().Diag(diag::err_drv_invalid_value) << "HV" << A->getValue();
278*0fca6ea1SDimitry Andric       }
279*0fca6ea1SDimitry Andric 
280*0fca6ea1SDimitry Andric       A->claim();
281*0fca6ea1SDimitry Andric       continue;
282*0fca6ea1SDimitry Andric     }
28381ad6265SDimitry Andric     DAL->append(A);
28481ad6265SDimitry Andric   }
285bdd1243dSDimitry Andric 
28681ad6265SDimitry Andric   // Add default validator version if not set.
28781ad6265SDimitry Andric   // TODO: remove this once read validator version from validator.
28881ad6265SDimitry Andric   if (!DAL->hasArg(options::OPT_dxil_validator_version)) {
28981ad6265SDimitry Andric     const StringRef DefaultValidatorVer = "1.7";
29081ad6265SDimitry Andric     DAL->AddSeparateArg(nullptr,
29181ad6265SDimitry Andric                         Opts.getOption(options::OPT_dxil_validator_version),
29281ad6265SDimitry Andric                         DefaultValidatorVer);
29381ad6265SDimitry Andric   }
294bdd1243dSDimitry Andric   if (!DAL->hasArg(options::OPT_O_Group)) {
295bdd1243dSDimitry Andric     DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), "3");
296bdd1243dSDimitry Andric   }
297*0fca6ea1SDimitry Andric 
29881ad6265SDimitry Andric   return DAL;
29981ad6265SDimitry Andric }
30006c3fb27SDimitry Andric 
30106c3fb27SDimitry Andric bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const {
30206c3fb27SDimitry Andric   if (Args.getLastArg(options::OPT_dxc_disable_validation))
30306c3fb27SDimitry Andric     return false;
30406c3fb27SDimitry Andric 
30506c3fb27SDimitry Andric   std::string DxvPath = GetProgramPath("dxv");
30606c3fb27SDimitry Andric   if (DxvPath != "dxv")
30706c3fb27SDimitry Andric     return true;
30806c3fb27SDimitry Andric 
30906c3fb27SDimitry Andric   getDriver().Diag(diag::warn_drv_dxc_missing_dxv);
31006c3fb27SDimitry Andric   return false;
31106c3fb27SDimitry Andric }
312