Coverage Report

Created: 2024-01-17 10:31

/src/llvm-project/clang/lib/Driver/ToolChains/HLSL.cpp
Line
Count
Source (jump to first uncovered line)
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
17
using namespace clang::driver;
18
using namespace clang::driver::tools;
19
using namespace clang::driver::toolchains;
20
using namespace clang;
21
using namespace llvm::opt;
22
using namespace llvm;
23
24
namespace {
25
26
const unsigned OfflineLibMinor = 0xF;
27
28
0
bool isLegalShaderModel(Triple &T) {
29
0
  if (T.getOS() != Triple::OSType::ShaderModel)
30
0
    return false;
31
32
0
  auto Version = T.getOSVersion();
33
0
  if (Version.getBuild())
34
0
    return false;
35
0
  if (Version.getSubminor())
36
0
    return false;
37
38
0
  auto Kind = T.getEnvironment();
39
40
0
  switch (Kind) {
41
0
  default:
42
0
    return false;
43
0
  case Triple::EnvironmentType::Vertex:
44
0
  case Triple::EnvironmentType::Hull:
45
0
  case Triple::EnvironmentType::Domain:
46
0
  case Triple::EnvironmentType::Geometry:
47
0
  case Triple::EnvironmentType::Pixel:
48
0
  case Triple::EnvironmentType::Compute: {
49
0
    VersionTuple MinVer(4, 0);
50
0
    return MinVer <= Version;
51
0
  } break;
52
0
  case Triple::EnvironmentType::Library: {
53
0
    VersionTuple SM6x(6, OfflineLibMinor);
54
0
    if (Version == SM6x)
55
0
      return true;
56
57
0
    VersionTuple MinVer(6, 3);
58
0
    return MinVer <= Version;
59
0
  } break;
60
0
  case Triple::EnvironmentType::Amplification:
61
0
  case Triple::EnvironmentType::Mesh: {
62
0
    VersionTuple MinVer(6, 5);
63
0
    return MinVer <= Version;
64
0
  } break;
65
0
  }
66
0
  return false;
67
0
}
68
69
0
std::optional<std::string> tryParseProfile(StringRef Profile) {
70
  // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
71
0
  SmallVector<StringRef, 3> Parts;
72
0
  Profile.split(Parts, "_");
73
0
  if (Parts.size() != 3)
74
0
    return std::nullopt;
75
76
0
  Triple::EnvironmentType Kind =
77
0
      StringSwitch<Triple::EnvironmentType>(Parts[0])
78
0
          .Case("ps", Triple::EnvironmentType::Pixel)
79
0
          .Case("vs", Triple::EnvironmentType::Vertex)
80
0
          .Case("gs", Triple::EnvironmentType::Geometry)
81
0
          .Case("hs", Triple::EnvironmentType::Hull)
82
0
          .Case("ds", Triple::EnvironmentType::Domain)
83
0
          .Case("cs", Triple::EnvironmentType::Compute)
84
0
          .Case("lib", Triple::EnvironmentType::Library)
85
0
          .Case("ms", Triple::EnvironmentType::Mesh)
86
0
          .Case("as", Triple::EnvironmentType::Amplification)
87
0
          .Default(Triple::EnvironmentType::UnknownEnvironment);
88
0
  if (Kind == Triple::EnvironmentType::UnknownEnvironment)
89
0
    return std::nullopt;
90
91
0
  unsigned long long Major = 0;
92
0
  if (llvm::getAsUnsignedInteger(Parts[1], 0, Major))
93
0
    return std::nullopt;
94
95
0
  unsigned long long Minor = 0;
96
0
  if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library)
97
0
    Minor = OfflineLibMinor;
98
0
  else if (llvm::getAsUnsignedInteger(Parts[2], 0, Minor))
99
0
    return std::nullopt;
100
101
  // dxil-unknown-shadermodel-hull
102
0
  llvm::Triple T;
103
0
  T.setArch(Triple::ArchType::dxil);
104
0
  T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() +
105
0
              VersionTuple(Major, Minor).getAsString());
106
0
  T.setEnvironment(Kind);
107
0
  if (isLegalShaderModel(T))
108
0
    return T.getTriple();
109
0
  else
110
0
    return std::nullopt;
111
0
}
112
113
0
bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) {
114
0
  VersionTuple Version;
115
0
  if (Version.tryParse(ValVersionStr) || Version.getBuild() ||
116
0
      Version.getSubminor() || !Version.getMinor()) {
117
0
    D.Diag(diag::err_drv_invalid_format_dxil_validator_version)
118
0
        << ValVersionStr;
119
0
    return false;
120
0
  }
121
122
0
  uint64_t Major = Version.getMajor();
123
0
  uint64_t Minor = *Version.getMinor();
124
0
  if (Major == 0 && Minor != 0) {
125
0
    D.Diag(diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr;
126
0
    return false;
127
0
  }
128
0
  VersionTuple MinVer(1, 0);
129
0
  if (Version < MinVer) {
130
0
    D.Diag(diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr;
131
0
    return false;
132
0
  }
133
0
  return true;
134
0
}
135
136
} // namespace
137
138
void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
139
                                          const InputInfo &Output,
140
                                          const InputInfoList &Inputs,
141
                                          const ArgList &Args,
142
0
                                          const char *LinkingOutput) const {
143
0
  std::string DxvPath = getToolChain().GetProgramPath("dxv");
144
0
  assert(DxvPath != "dxv" && "cannot find dxv");
145
146
0
  ArgStringList CmdArgs;
147
0
  assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
148
0
  const InputInfo &Input = Inputs[0];
149
0
  assert(Input.isFilename() && "Unexpected verify input");
150
  // Grabbing the output of the earlier cc1 run.
151
0
  CmdArgs.push_back(Input.getFilename());
152
  // Use the same name as output.
153
0
  CmdArgs.push_back("-o");
154
0
  CmdArgs.push_back(Input.getFilename());
155
156
0
  const char *Exec = Args.MakeArgString(DxvPath);
157
0
  C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
158
0
                                         Exec, CmdArgs, Inputs, Input));
159
0
}
160
161
/// DirectX Toolchain
162
HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
163
                             const ArgList &Args)
164
0
    : ToolChain(D, Triple, Args) {
165
0
  if (Args.hasArg(options::OPT_dxc_validator_path_EQ))
166
0
    getProgramPaths().push_back(
167
0
        Args.getLastArgValue(options::OPT_dxc_validator_path_EQ).str());
168
0
}
169
170
Tool *clang::driver::toolchains::HLSLToolChain::getTool(
171
0
    Action::ActionClass AC) const {
172
0
  switch (AC) {
173
0
  case Action::BinaryAnalyzeJobClass:
174
0
    if (!Validator)
175
0
      Validator.reset(new tools::hlsl::Validator(*this));
176
0
    return Validator.get();
177
0
  default:
178
0
    return ToolChain::getTool(AC);
179
0
  }
180
0
}
181
182
std::optional<std::string>
183
clang::driver::toolchains::HLSLToolChain::parseTargetProfile(
184
0
    StringRef TargetProfile) {
185
0
  return tryParseProfile(TargetProfile);
186
0
}
187
188
DerivedArgList *
189
HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
190
0
                             Action::OffloadKind DeviceOffloadKind) const {
191
0
  DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
192
193
0
  const OptTable &Opts = getDriver().getOpts();
194
195
0
  for (Arg *A : Args) {
196
0
    if (A->getOption().getID() == options::OPT_dxil_validator_version) {
197
0
      StringRef ValVerStr = A->getValue();
198
0
      std::string ErrorMsg;
199
0
      if (!isLegalValidatorVersion(ValVerStr, getDriver()))
200
0
        continue;
201
0
    }
202
0
    if (A->getOption().getID() == options::OPT_dxc_entrypoint) {
203
0
      DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_hlsl_entrypoint),
204
0
                          A->getValue());
205
0
      A->claim();
206
0
      continue;
207
0
    }
208
0
    if (A->getOption().getID() == options::OPT__SLASH_O) {
209
0
      StringRef OStr = A->getValue();
210
0
      if (OStr == "d") {
211
0
        DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_O0));
212
0
        A->claim();
213
0
        continue;
214
0
      } else {
215
0
        DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), OStr);
216
0
        A->claim();
217
0
        continue;
218
0
      }
219
0
    }
220
0
    if (A->getOption().getID() == options::OPT_emit_pristine_llvm) {
221
      // Translate fcgl into -S -emit-llvm and -disable-llvm-passes.
222
0
      DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_S));
223
0
      DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm));
224
0
      DAL->AddFlagArg(nullptr,
225
0
                      Opts.getOption(options::OPT_disable_llvm_passes));
226
0
      A->claim();
227
0
      continue;
228
0
    }
229
0
    DAL->append(A);
230
0
  }
231
232
  // Add default validator version if not set.
233
  // TODO: remove this once read validator version from validator.
234
0
  if (!DAL->hasArg(options::OPT_dxil_validator_version)) {
235
0
    const StringRef DefaultValidatorVer = "1.7";
236
0
    DAL->AddSeparateArg(nullptr,
237
0
                        Opts.getOption(options::OPT_dxil_validator_version),
238
0
                        DefaultValidatorVer);
239
0
  }
240
0
  if (!DAL->hasArg(options::OPT_O_Group)) {
241
0
    DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), "3");
242
0
  }
243
  // FIXME: add validation for enable_16bit_types should be after HLSL 2018 and
244
  // shader model 6.2.
245
  // See: https://github.com/llvm/llvm-project/issues/57876
246
0
  return DAL;
247
0
}
248
249
0
bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const {
250
0
  if (Args.getLastArg(options::OPT_dxc_disable_validation))
251
0
    return false;
252
253
0
  std::string DxvPath = GetProgramPath("dxv");
254
0
  if (DxvPath != "dxv")
255
0
    return true;
256
257
0
  getDriver().Diag(diag::warn_drv_dxc_missing_dxv);
258
0
  return false;
259
0
}