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/DriverDiagnostic.h"
12 #include "llvm/ADT/StringSwitch.h"
13 #include "llvm/ADT/Triple.h"
14 
15 using namespace clang::driver;
16 using namespace clang::driver::tools;
17 using namespace clang::driver::toolchains;
18 using namespace clang;
19 using namespace llvm::opt;
20 using namespace llvm;
21 
22 namespace {
23 
24 const unsigned OfflineLibMinor = 0xF;
25 
26 bool isLegalShaderModel(Triple &T) {
27   if (T.getOS() != Triple::OSType::ShaderModel)
28     return false;
29 
30   auto Version = T.getOSVersion();
31   if (Version.getBuild())
32     return false;
33   if (Version.getSubminor())
34     return false;
35 
36   auto Kind = T.getEnvironment();
37 
38   switch (Kind) {
39   default:
40     return false;
41   case Triple::EnvironmentType::Vertex:
42   case Triple::EnvironmentType::Hull:
43   case Triple::EnvironmentType::Domain:
44   case Triple::EnvironmentType::Geometry:
45   case Triple::EnvironmentType::Pixel:
46   case Triple::EnvironmentType::Compute: {
47     VersionTuple MinVer(4, 0);
48     return MinVer <= Version;
49   } break;
50   case Triple::EnvironmentType::Library: {
51     VersionTuple SM6x(6, OfflineLibMinor);
52     if (Version == SM6x)
53       return true;
54 
55     VersionTuple MinVer(6, 3);
56     return MinVer <= Version;
57   } break;
58   case Triple::EnvironmentType::Amplification:
59   case Triple::EnvironmentType::Mesh: {
60     VersionTuple MinVer(6, 5);
61     return MinVer <= Version;
62   } break;
63   }
64   return false;
65 }
66 
67 std::string tryParseProfile(StringRef Profile) {
68   // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
69   SmallVector<StringRef, 3> Parts;
70   Profile.split(Parts, "_");
71   if (Parts.size() != 3)
72     return "";
73 
74   Triple::EnvironmentType Kind =
75       StringSwitch<Triple::EnvironmentType>(Parts[0])
76           .Case("ps", Triple::EnvironmentType::Pixel)
77           .Case("vs", Triple::EnvironmentType::Vertex)
78           .Case("gs", Triple::EnvironmentType::Geometry)
79           .Case("hs", Triple::EnvironmentType::Hull)
80           .Case("ds", Triple::EnvironmentType::Domain)
81           .Case("cs", Triple::EnvironmentType::Compute)
82           .Case("lib", Triple::EnvironmentType::Library)
83           .Case("ms", Triple::EnvironmentType::Mesh)
84           .Case("as", Triple::EnvironmentType::Amplification)
85           .Default(Triple::EnvironmentType::UnknownEnvironment);
86   if (Kind == Triple::EnvironmentType::UnknownEnvironment)
87     return "";
88 
89   unsigned long long Major = 0;
90   if (llvm::getAsUnsignedInteger(Parts[1], 0, Major))
91     return "";
92 
93   unsigned long long Minor = 0;
94   if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library)
95     Minor = OfflineLibMinor;
96   else if (llvm::getAsUnsignedInteger(Parts[2], 0, Minor))
97     return "";
98 
99   // dxil-unknown-shadermodel-hull
100   llvm::Triple T;
101   T.setArch(Triple::ArchType::dxil);
102   T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() +
103               VersionTuple(Major, Minor).getAsString());
104   T.setEnvironment(Kind);
105   if (isLegalShaderModel(T))
106     return T.getTriple();
107   else
108     return "";
109 }
110 
111 bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) {
112   VersionTuple Version;
113   if (Version.tryParse(ValVersionStr) || Version.getBuild() ||
114       Version.getSubminor() || !Version.getMinor()) {
115     D.Diag(diag::err_drv_invalid_format_dxil_validator_version)
116         << ValVersionStr;
117     return false;
118   }
119 
120   uint64_t Major = Version.getMajor();
121   uint64_t Minor = Version.getMinor().getValue();
122   if (Major == 0 && Minor != 0) {
123     D.Diag(diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr;
124     return false;
125   }
126   VersionTuple MinVer(1, 0);
127   if (Version < MinVer) {
128     D.Diag(diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr;
129     return false;
130   }
131   return true;
132 }
133 
134 } // namespace
135 
136 /// DirectX Toolchain
137 HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
138                              const ArgList &Args)
139     : ToolChain(D, Triple, Args) {}
140 
141 std::string
142 HLSLToolChain::ComputeEffectiveClangTriple(const ArgList &Args,
143                                            types::ID InputType) const {
144   if (Arg *A = Args.getLastArg(options::OPT_target_profile)) {
145     StringRef Profile = A->getValue();
146     std::string Triple = tryParseProfile(Profile);
147     if (Triple == "") {
148       getDriver().Diag(diag::err_drv_invalid_directx_shader_module) << Profile;
149       Triple = ToolChain::ComputeEffectiveClangTriple(Args, InputType);
150     }
151     A->claim();
152     return Triple;
153   } else {
154     return ToolChain::ComputeEffectiveClangTriple(Args, InputType);
155   }
156 }
157 
158 DerivedArgList *
159 HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
160                              Action::OffloadKind DeviceOffloadKind) const {
161   DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
162 
163   const OptTable &Opts = getDriver().getOpts();
164 
165   for (Arg *A : Args) {
166     if (A->getOption().getID() == options::OPT_dxil_validator_version) {
167       StringRef ValVerStr = A->getValue();
168       std::string ErrorMsg;
169       if (!isLegalValidatorVersion(ValVerStr, getDriver()))
170         continue;
171     }
172     if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
173       // Translate fcgl into -S -emit-llvm and -disable-llvm-passes.
174       DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_S));
175       DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm));
176       DAL->AddFlagArg(nullptr,
177                       Opts.getOption(options::OPT_disable_llvm_passes));
178       A->claim();
179       continue;
180     }
181     DAL->append(A);
182   }
183   // Add default validator version if not set.
184   // TODO: remove this once read validator version from validator.
185   if (!DAL->hasArg(options::OPT_dxil_validator_version)) {
186     const StringRef DefaultValidatorVer = "1.7";
187     DAL->AddSeparateArg(nullptr,
188                         Opts.getOption(options::OPT_dxil_validator_version),
189                         DefaultValidatorVer);
190   }
191   return DAL;
192 }
193