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
15using namespace clang::driver;
16using namespace clang::driver::tools;
17using namespace clang::driver::toolchains;
18using namespace clang;
19using namespace llvm::opt;
20using namespace llvm;
21
22namespace {
23
24const unsigned OfflineLibMinor = 0xF;
25
26bool 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
67std::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 std::nullopt;
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 std::nullopt;
88
89  unsigned long long Major = 0;
90  if (llvm::getAsUnsignedInteger(Parts[1], 0, Major))
91    return std::nullopt;
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 std::nullopt;
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 std::nullopt;
109}
110
111bool 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
137HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
138                             const ArgList &Args)
139    : ToolChain(D, Triple, Args) {}
140
141std::optional<std::string>
142clang::driver::toolchains::HLSLToolChain::parseTargetProfile(
143    StringRef TargetProfile) {
144  return tryParseProfile(TargetProfile);
145}
146
147DerivedArgList *
148HLSLToolChain::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_dxc_entrypoint) {
162      DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_hlsl_entrypoint),
163                          A->getValue());
164      A->claim();
165      continue;
166    }
167    if (A->getOption().getID() == options::OPT__SLASH_O) {
168      StringRef OStr = A->getValue();
169      if (OStr == "d") {
170        DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_O0));
171        A->claim();
172        continue;
173      } else {
174        DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), OStr);
175        A->claim();
176        continue;
177      }
178    }
179    if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
180      // Translate fcgl into -S -emit-llvm and -disable-llvm-passes.
181      DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_S));
182      DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm));
183      DAL->AddFlagArg(nullptr,
184                      Opts.getOption(options::OPT_disable_llvm_passes));
185      A->claim();
186      continue;
187    }
188    DAL->append(A);
189  }
190
191  if (DAL->hasArg(options::OPT_o)) {
192    // When run the whole pipeline.
193    if (!DAL->hasArg(options::OPT_emit_llvm))
194      // Emit obj if write to file.
195      DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_obj));
196  } else
197    DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_o), "-");
198
199  // Add default validator version if not set.
200  // TODO: remove this once read validator version from validator.
201  if (!DAL->hasArg(options::OPT_dxil_validator_version)) {
202    const StringRef DefaultValidatorVer = "1.7";
203    DAL->AddSeparateArg(nullptr,
204                        Opts.getOption(options::OPT_dxil_validator_version),
205                        DefaultValidatorVer);
206  }
207  if (!DAL->hasArg(options::OPT_O_Group)) {
208    DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), "3");
209  }
210  // FIXME: add validation for enable_16bit_types should be after HLSL 2018 and
211  // shader model 6.2.
212  // See: https://github.com/llvm/llvm-project/issues/57876
213  return DAL;
214}
215