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 llvm::Optional<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 NoneType();
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 NoneType();
88 
89   unsigned long long Major = 0;
90   if (llvm::getAsUnsignedInteger(Parts[1], 0, Major))
91     return NoneType();
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 NoneType();
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 NoneType();
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();
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 llvm::Optional<std::string>
142 clang::driver::toolchains::HLSLToolChain::parseTargetProfile(
143     StringRef TargetProfile) {
144   return tryParseProfile(TargetProfile);
145 }
146 
147 DerivedArgList *
148 HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
149                              Action::OffloadKind DeviceOffloadKind) const {
150   DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
151 
152   const OptTable &Opts = getDriver().getOpts();
153 
154   for (Arg *A : Args) {
155     if (A->getOption().getID() == options::OPT_dxil_validator_version) {
156       StringRef ValVerStr = A->getValue();
157       std::string ErrorMsg;
158       if (!isLegalValidatorVersion(ValVerStr, getDriver()))
159         continue;
160     }
161     if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
162       // Translate fcgl into -S -emit-llvm and -disable-llvm-passes.
163       DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_S));
164       DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm));
165       DAL->AddFlagArg(nullptr,
166                       Opts.getOption(options::OPT_disable_llvm_passes));
167       A->claim();
168       continue;
169     }
170     DAL->append(A);
171   }
172   // Add default validator version if not set.
173   // TODO: remove this once read validator version from validator.
174   if (!DAL->hasArg(options::OPT_dxil_validator_version)) {
175     const StringRef DefaultValidatorVer = "1.7";
176     DAL->AddSeparateArg(nullptr,
177                         Opts.getOption(options::OPT_dxil_validator_version),
178                         DefaultValidatorVer);
179   }
180   // FIXME: add validation for enable_16bit_types should be after HLSL 2018 and
181   // shader model 6.2.
182   return DAL;
183 }
184