| 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 "clang/Driver/CommonArgs.h" |
| 11 | #include "clang/Driver/Compilation.h" |
| 12 | #include "clang/Driver/Job.h" |
| 13 | #include "llvm/ADT/StringSwitch.h" |
| 14 | #include "llvm/TargetParser/Triple.h" |
| 15 | #include <regex> |
| 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 | bool isLegalShaderModel(Triple &T) { |
| 29 | if (T.getOS() != Triple::OSType::ShaderModel) |
| 30 | return false; |
| 31 | |
| 32 | auto Version = T.getOSVersion(); |
| 33 | if (Version.getBuild()) |
| 34 | return false; |
| 35 | if (Version.getSubminor()) |
| 36 | return false; |
| 37 | |
| 38 | auto Kind = T.getEnvironment(); |
| 39 | |
| 40 | switch (Kind) { |
| 41 | default: |
| 42 | return false; |
| 43 | case Triple::EnvironmentType::Vertex: |
| 44 | case Triple::EnvironmentType::Hull: |
| 45 | case Triple::EnvironmentType::Domain: |
| 46 | case Triple::EnvironmentType::Geometry: |
| 47 | case Triple::EnvironmentType::Pixel: |
| 48 | case Triple::EnvironmentType::Compute: { |
| 49 | VersionTuple MinVer(4, 0); |
| 50 | return MinVer <= Version; |
| 51 | } break; |
| 52 | case Triple::EnvironmentType::Library: { |
| 53 | VersionTuple SM6x(6, OfflineLibMinor); |
| 54 | if (Version == SM6x) |
| 55 | return true; |
| 56 | |
| 57 | VersionTuple MinVer(6, 3); |
| 58 | return MinVer <= Version; |
| 59 | } break; |
| 60 | case Triple::EnvironmentType::Amplification: |
| 61 | case Triple::EnvironmentType::Mesh: { |
| 62 | VersionTuple MinVer(6, 5); |
| 63 | return MinVer <= Version; |
| 64 | } break; |
| 65 | case Triple::EnvironmentType::RootSignature: |
| 66 | VersionTuple MinVer(1, 0); |
| 67 | VersionTuple MaxVer(1, 2); |
| 68 | return MinVer <= Version && Version <= MaxVer; |
| 69 | } |
| 70 | return false; |
| 71 | } |
| 72 | |
| 73 | std::optional<llvm::Triple> tryParseTriple(StringRef Profile) { |
| 74 | // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor] |
| 75 | SmallVector<StringRef, 3> Parts; |
| 76 | Profile.split(A&: Parts, Separator: "_" ); |
| 77 | if (Parts.size() != 3) |
| 78 | return std::nullopt; |
| 79 | |
| 80 | Triple::EnvironmentType Kind = |
| 81 | StringSwitch<Triple::EnvironmentType>(Parts[0]) |
| 82 | .Case(S: "ps" , Value: Triple::EnvironmentType::Pixel) |
| 83 | .Case(S: "vs" , Value: Triple::EnvironmentType::Vertex) |
| 84 | .Case(S: "gs" , Value: Triple::EnvironmentType::Geometry) |
| 85 | .Case(S: "hs" , Value: Triple::EnvironmentType::Hull) |
| 86 | .Case(S: "ds" , Value: Triple::EnvironmentType::Domain) |
| 87 | .Case(S: "cs" , Value: Triple::EnvironmentType::Compute) |
| 88 | .Case(S: "lib" , Value: Triple::EnvironmentType::Library) |
| 89 | .Case(S: "ms" , Value: Triple::EnvironmentType::Mesh) |
| 90 | .Case(S: "as" , Value: Triple::EnvironmentType::Amplification) |
| 91 | .Case(S: "rootsig" , Value: Triple::EnvironmentType::RootSignature) |
| 92 | .Default(Value: Triple::EnvironmentType::UnknownEnvironment); |
| 93 | if (Kind == Triple::EnvironmentType::UnknownEnvironment) |
| 94 | return std::nullopt; |
| 95 | |
| 96 | unsigned long long Major = 0; |
| 97 | if (llvm::getAsUnsignedInteger(Str: Parts[1], Radix: 0, Result&: Major)) |
| 98 | return std::nullopt; |
| 99 | |
| 100 | unsigned long long Minor = 0; |
| 101 | if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library) |
| 102 | Minor = OfflineLibMinor; |
| 103 | else if (llvm::getAsUnsignedInteger(Str: Parts[2], Radix: 0, Result&: Minor)) |
| 104 | return std::nullopt; |
| 105 | |
| 106 | // Determine DXIL version using the minor version number of Shader |
| 107 | // Model version specified in target profile. Prior to decoupling DXIL version |
| 108 | // numbering from that of Shader Model DXIL version 1.Y corresponds to SM 6.Y. |
| 109 | // E.g., dxilv1.Y-unknown-shadermodelX.Y-hull |
| 110 | llvm::Triple T; |
| 111 | Triple::SubArchType SubArch = llvm::Triple::NoSubArch; |
| 112 | switch (Minor) { |
| 113 | case 0: |
| 114 | SubArch = llvm::Triple::DXILSubArch_v1_0; |
| 115 | break; |
| 116 | case 1: |
| 117 | SubArch = llvm::Triple::DXILSubArch_v1_1; |
| 118 | break; |
| 119 | case 2: |
| 120 | SubArch = llvm::Triple::DXILSubArch_v1_2; |
| 121 | break; |
| 122 | case 3: |
| 123 | SubArch = llvm::Triple::DXILSubArch_v1_3; |
| 124 | break; |
| 125 | case 4: |
| 126 | SubArch = llvm::Triple::DXILSubArch_v1_4; |
| 127 | break; |
| 128 | case 5: |
| 129 | SubArch = llvm::Triple::DXILSubArch_v1_5; |
| 130 | break; |
| 131 | case 6: |
| 132 | SubArch = llvm::Triple::DXILSubArch_v1_6; |
| 133 | break; |
| 134 | case 7: |
| 135 | SubArch = llvm::Triple::DXILSubArch_v1_7; |
| 136 | break; |
| 137 | case 8: |
| 138 | SubArch = llvm::Triple::DXILSubArch_v1_8; |
| 139 | break; |
| 140 | case 9: |
| 141 | SubArch = llvm::Triple::DXILSubArch_v1_9; |
| 142 | break; |
| 143 | case OfflineLibMinor: |
| 144 | // Always consider minor version x as the latest supported DXIL version |
| 145 | SubArch = llvm::Triple::LatestDXILSubArch; |
| 146 | break; |
| 147 | default: |
| 148 | // No DXIL Version corresponding to specified Shader Model version found |
| 149 | return std::nullopt; |
| 150 | } |
| 151 | T.setArch(Kind: Triple::ArchType::dxil, SubArch); |
| 152 | T.setOSName(Triple::getOSTypeName(Kind: Triple::OSType::ShaderModel).str() + |
| 153 | VersionTuple(Major, Minor).getAsString()); |
| 154 | T.setEnvironment(Kind); |
| 155 | |
| 156 | return T; |
| 157 | } |
| 158 | |
| 159 | std::optional<std::string> tryParseProfile(StringRef Profile) { |
| 160 | std::optional<llvm::Triple> MaybeT = tryParseTriple(Profile); |
| 161 | if (MaybeT && isLegalShaderModel(T&: *MaybeT)) |
| 162 | return MaybeT->getTriple(); |
| 163 | else |
| 164 | return std::nullopt; |
| 165 | } |
| 166 | |
| 167 | bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) { |
| 168 | VersionTuple Version; |
| 169 | if (Version.tryParse(string: ValVersionStr) || Version.getBuild() || |
| 170 | Version.getSubminor() || !Version.getMinor()) { |
| 171 | D.Diag(DiagID: diag::err_drv_invalid_format_dxil_validator_version) |
| 172 | << ValVersionStr; |
| 173 | return false; |
| 174 | } |
| 175 | |
| 176 | uint64_t Major = Version.getMajor(); |
| 177 | uint64_t Minor = *Version.getMinor(); |
| 178 | if (Major == 0 && Minor != 0) { |
| 179 | D.Diag(DiagID: diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr; |
| 180 | return false; |
| 181 | } |
| 182 | VersionTuple MinVer(1, 0); |
| 183 | if (Version < MinVer) { |
| 184 | D.Diag(DiagID: diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr; |
| 185 | return false; |
| 186 | } |
| 187 | return true; |
| 188 | } |
| 189 | |
| 190 | void getSpirvExtOperand(StringRef SpvExtensionArg, raw_ostream &out) { |
| 191 | // The extensions that are commented out are supported in DXC, but the SPIR-V |
| 192 | // backend does not know about them yet. |
| 193 | static const std::vector<StringRef> DxcSupportedExtensions = { |
| 194 | "SPV_KHR_16bit_storage" , |
| 195 | "SPV_KHR_device_group" , |
| 196 | "SPV_KHR_fragment_shading_rate" , |
| 197 | "SPV_KHR_multiview" , |
| 198 | "SPV_KHR_post_depth_coverage" , |
| 199 | "SPV_KHR_non_semantic_info" , |
| 200 | "SPV_KHR_shader_draw_parameters" , |
| 201 | "SPV_KHR_ray_tracing" , |
| 202 | "SPV_KHR_shader_clock" , |
| 203 | "SPV_EXT_demote_to_helper_invocation" , |
| 204 | "SPV_EXT_descriptor_indexing" , |
| 205 | "SPV_EXT_fragment_fully_covered" , |
| 206 | "SPV_EXT_fragment_invocation_density" , |
| 207 | "SPV_EXT_fragment_shader_interlock" , |
| 208 | "SPV_EXT_mesh_shader" , |
| 209 | "SPV_EXT_shader_stencil_export" , |
| 210 | "SPV_EXT_shader_viewport_index_layer" , |
| 211 | // "SPV_AMD_shader_early_and_late_fragment_tests", |
| 212 | "SPV_GOOGLE_hlsl_functionality1" , |
| 213 | "SPV_GOOGLE_user_type" , |
| 214 | "SPV_KHR_ray_query" , |
| 215 | "SPV_EXT_shader_image_int64" , |
| 216 | "SPV_KHR_fragment_shader_barycentric" , |
| 217 | "SPV_KHR_physical_storage_buffer" , |
| 218 | "SPV_KHR_vulkan_memory_model" , |
| 219 | // "SPV_KHR_compute_shader_derivatives", |
| 220 | "SPV_KHR_maximal_reconvergence" , |
| 221 | "SPV_KHR_float_controls" , |
| 222 | "SPV_NV_shader_subgroup_partitioned" , |
| 223 | // "SPV_KHR_quad_control" |
| 224 | }; |
| 225 | |
| 226 | if (SpvExtensionArg.starts_with(Prefix: "SPV_" )) { |
| 227 | out << "+" << SpvExtensionArg; |
| 228 | return; |
| 229 | } |
| 230 | |
| 231 | if (SpvExtensionArg.compare_insensitive(RHS: "DXC" ) == 0) { |
| 232 | bool first = true; |
| 233 | for (StringRef E : DxcSupportedExtensions) { |
| 234 | if (!first) |
| 235 | out << "," ; |
| 236 | else |
| 237 | first = false; |
| 238 | out << "+" << E; |
| 239 | } |
| 240 | return; |
| 241 | } |
| 242 | out << SpvExtensionArg; |
| 243 | } |
| 244 | |
| 245 | SmallString<1024> getSpirvExtArg(ArrayRef<std::string> SpvExtensionArgs) { |
| 246 | if (SpvExtensionArgs.empty()) { |
| 247 | return StringRef("-spirv-ext=all" ); |
| 248 | } |
| 249 | |
| 250 | llvm::SmallString<1024> LlvmOption; |
| 251 | raw_svector_ostream out(LlvmOption); |
| 252 | |
| 253 | out << "-spirv-ext=" ; |
| 254 | getSpirvExtOperand(SpvExtensionArg: SpvExtensionArgs[0], out); |
| 255 | |
| 256 | SpvExtensionArgs = SpvExtensionArgs.slice(N: 1); |
| 257 | for (StringRef Extension : SpvExtensionArgs) { |
| 258 | out << "," ; |
| 259 | getSpirvExtOperand(SpvExtensionArg: Extension, out); |
| 260 | } |
| 261 | return LlvmOption; |
| 262 | } |
| 263 | |
| 264 | bool isValidSPIRVExtensionName(const std::string &str) { |
| 265 | std::regex pattern("dxc|DXC|khr|KHR|SPV_[a-zA-Z0-9_]+" ); |
| 266 | return std::regex_match(s: str, re: pattern); |
| 267 | } |
| 268 | |
| 269 | // SPIRV extension names are of the form `SPV_[a-zA-Z0-9_]+`. We want to |
| 270 | // disallow obviously invalid names to avoid issues when parsing `spirv-ext`. |
| 271 | bool checkExtensionArgsAreValid(ArrayRef<std::string> SpvExtensionArgs, |
| 272 | const Driver &Driver) { |
| 273 | bool AllValid = true; |
| 274 | for (auto Extension : SpvExtensionArgs) { |
| 275 | if (!isValidSPIRVExtensionName(str: Extension)) { |
| 276 | Driver.Diag(DiagID: diag::err_drv_invalid_value) |
| 277 | << "-fspv-extension" << Extension; |
| 278 | AllValid = false; |
| 279 | } |
| 280 | } |
| 281 | return AllValid; |
| 282 | } |
| 283 | |
| 284 | bool isRootSignatureTarget(StringRef Profile) { |
| 285 | if (std::optional<llvm::Triple> T = tryParseTriple(Profile)) |
| 286 | return T->getEnvironment() == Triple::EnvironmentType::RootSignature; |
| 287 | return false; |
| 288 | } |
| 289 | |
| 290 | bool isRootSignatureTarget(DerivedArgList &Args) { |
| 291 | if (const Arg *A = Args.getLastArg(Ids: options::OPT_target_profile)) |
| 292 | return isRootSignatureTarget(Profile: A->getValue()); |
| 293 | return false; |
| 294 | } |
| 295 | |
| 296 | } // namespace |
| 297 | |
| 298 | void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA, |
| 299 | const InputInfo &Output, |
| 300 | const InputInfoList &Inputs, |
| 301 | const ArgList &Args, |
| 302 | const char *LinkingOutput) const { |
| 303 | std::string DxvPath = getToolChain().GetProgramPath(Name: "dxv" ); |
| 304 | assert(DxvPath != "dxv" && "cannot find dxv" ); |
| 305 | |
| 306 | ArgStringList CmdArgs; |
| 307 | assert(Inputs.size() == 1 && "Unable to handle multiple inputs." ); |
| 308 | const InputInfo &Input = Inputs[0]; |
| 309 | CmdArgs.push_back(Elt: Input.getFilename()); |
| 310 | CmdArgs.push_back(Elt: "-o" ); |
| 311 | CmdArgs.push_back(Elt: Output.getFilename()); |
| 312 | |
| 313 | const char *Exec = Args.MakeArgString(Str: DxvPath); |
| 314 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(), |
| 315 | args&: Exec, args&: CmdArgs, args: Inputs, args: Input)); |
| 316 | } |
| 317 | |
| 318 | void tools::hlsl::MetalConverter::ConstructJob( |
| 319 | Compilation &C, const JobAction &JA, const InputInfo &Output, |
| 320 | const InputInfoList &Inputs, const ArgList &Args, |
| 321 | const char *LinkingOutput) const { |
| 322 | std::string MSCPath = getToolChain().GetProgramPath(Name: "metal-shaderconverter" ); |
| 323 | ArgStringList CmdArgs; |
| 324 | assert(Inputs.size() == 1 && "Unable to handle multiple inputs." ); |
| 325 | const InputInfo &Input = Inputs[0]; |
| 326 | CmdArgs.push_back(Elt: Input.getFilename()); |
| 327 | CmdArgs.push_back(Elt: "-o" ); |
| 328 | CmdArgs.push_back(Elt: Output.getFilename()); |
| 329 | |
| 330 | const char *Exec = Args.MakeArgString(Str: MSCPath); |
| 331 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(), |
| 332 | args&: Exec, args&: CmdArgs, args: Inputs, args: Input)); |
| 333 | } |
| 334 | |
| 335 | void tools::hlsl::LLVMObjcopy::ConstructJob(Compilation &C, const JobAction &JA, |
| 336 | const InputInfo &Output, |
| 337 | const InputInfoList &Inputs, |
| 338 | const ArgList &Args, |
| 339 | const char *LinkingOutput) const { |
| 340 | |
| 341 | std::string ObjcopyPath = getToolChain().GetProgramPath(Name: "llvm-objcopy" ); |
| 342 | const char *Exec = Args.MakeArgString(Str: ObjcopyPath); |
| 343 | |
| 344 | ArgStringList CmdArgs; |
| 345 | assert(Inputs.size() == 1 && "Unable to handle multiple inputs." ); |
| 346 | const InputInfo &Input = Inputs[0]; |
| 347 | CmdArgs.push_back(Elt: Input.getFilename()); |
| 348 | CmdArgs.push_back(Elt: Output.getFilename()); |
| 349 | |
| 350 | if (Args.hasArg(Ids: options::OPT_dxc_strip_rootsignature)) { |
| 351 | const char *StripRS = Args.MakeArgString(Str: "--remove-section=RTS0" ); |
| 352 | CmdArgs.push_back(Elt: StripRS); |
| 353 | } |
| 354 | |
| 355 | if (Arg *Arg = Args.getLastArg(Ids: options::OPT_dxc_Frs)) { |
| 356 | const char *Frs = |
| 357 | Args.MakeArgString(Str: "--extract-section=RTS0=" + Twine(Arg->getValue())); |
| 358 | CmdArgs.push_back(Elt: Frs); |
| 359 | } |
| 360 | |
| 361 | if (const Arg *A = Args.getLastArg(Ids: options::OPT_target_profile)) |
| 362 | if (isRootSignatureTarget(Profile: A->getValue())) { |
| 363 | const char *Fos = Args.MakeArgString(Str: "--only-section=RTS0" ); |
| 364 | CmdArgs.push_back(Elt: Fos); |
| 365 | } |
| 366 | |
| 367 | assert(CmdArgs.size() > 2 && "Unnecessary invocation of objcopy." ); |
| 368 | |
| 369 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, args: ResponseFileSupport::None(), |
| 370 | args&: Exec, args&: CmdArgs, args: Inputs, args: Input)); |
| 371 | } |
| 372 | |
| 373 | /// DirectX Toolchain |
| 374 | HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple, |
| 375 | const ArgList &Args) |
| 376 | : ToolChain(D, Triple, Args) { |
| 377 | if (Args.hasArg(Ids: options::OPT_dxc_validator_path_EQ)) |
| 378 | getProgramPaths().push_back( |
| 379 | Elt: Args.getLastArgValue(Id: options::OPT_dxc_validator_path_EQ).str()); |
| 380 | } |
| 381 | |
| 382 | Tool *clang::driver::toolchains::HLSLToolChain::getTool( |
| 383 | Action::ActionClass AC) const { |
| 384 | switch (AC) { |
| 385 | case Action::BinaryAnalyzeJobClass: |
| 386 | if (!Validator) |
| 387 | Validator.reset(p: new tools::hlsl::Validator(*this)); |
| 388 | return Validator.get(); |
| 389 | case Action::BinaryTranslatorJobClass: |
| 390 | if (!MetalConverter) |
| 391 | MetalConverter.reset(p: new tools::hlsl::MetalConverter(*this)); |
| 392 | return MetalConverter.get(); |
| 393 | case Action::ObjcopyJobClass: |
| 394 | if (!LLVMObjcopy) |
| 395 | LLVMObjcopy.reset(p: new tools::hlsl::LLVMObjcopy(*this)); |
| 396 | return LLVMObjcopy.get(); |
| 397 | default: |
| 398 | return ToolChain::getTool(AC); |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | std::optional<std::string> |
| 403 | clang::driver::toolchains::HLSLToolChain::parseTargetProfile( |
| 404 | StringRef TargetProfile) { |
| 405 | return tryParseProfile(Profile: TargetProfile); |
| 406 | } |
| 407 | |
| 408 | DerivedArgList * |
| 409 | HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, |
| 410 | Action::OffloadKind DeviceOffloadKind) const { |
| 411 | DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs()); |
| 412 | |
| 413 | const OptTable &Opts = getDriver().getOpts(); |
| 414 | |
| 415 | if (Args.hasArg(Ids: options::OPT_dxc_col_major) && |
| 416 | Args.hasArg(Ids: options::OPT_dxc_row_major)) |
| 417 | getDriver().Diag(DiagID: diag::err_drv_dxc_invalid_matrix_layout); |
| 418 | |
| 419 | for (Arg *A : Args) { |
| 420 | if (A->getOption().getID() == options::OPT_dxc_all_resources_bound) { |
| 421 | DAL->AddFlagArg(BaseArg: nullptr, |
| 422 | Opt: Opts.getOption(Opt: options::OPT_hlsl_all_resources_bound)); |
| 423 | A->claim(); |
| 424 | continue; |
| 425 | } |
| 426 | if (A->getOption().getID() == options::OPT_dxil_validator_version) { |
| 427 | StringRef ValVerStr = A->getValue(); |
| 428 | if (!isLegalValidatorVersion(ValVersionStr: ValVerStr, D: getDriver())) |
| 429 | continue; |
| 430 | } |
| 431 | if (A->getOption().getID() == options::OPT_dxc_entrypoint) { |
| 432 | DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_hlsl_entrypoint), |
| 433 | Value: A->getValue()); |
| 434 | A->claim(); |
| 435 | continue; |
| 436 | } |
| 437 | if (A->getOption().getID() == options::OPT_dxc_rootsig_ver) { |
| 438 | DAL->AddJoinedArg(BaseArg: nullptr, |
| 439 | Opt: Opts.getOption(Opt: options::OPT_fdx_rootsignature_version), |
| 440 | Value: A->getValue()); |
| 441 | A->claim(); |
| 442 | continue; |
| 443 | } |
| 444 | if (A->getOption().getID() == options::OPT_dxc_rootsig_define) { |
| 445 | DAL->AddJoinedArg(BaseArg: nullptr, |
| 446 | Opt: Opts.getOption(Opt: options::OPT_fdx_rootsignature_define), |
| 447 | Value: A->getValue()); |
| 448 | A->claim(); |
| 449 | continue; |
| 450 | } |
| 451 | if (A->getOption().getID() == options::OPT__SLASH_O) { |
| 452 | StringRef OStr = A->getValue(); |
| 453 | if (OStr == "d" ) { |
| 454 | DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_O0)); |
| 455 | A->claim(); |
| 456 | continue; |
| 457 | } else { |
| 458 | DAL->AddJoinedArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_O), Value: OStr); |
| 459 | A->claim(); |
| 460 | continue; |
| 461 | } |
| 462 | } |
| 463 | if (A->getOption().getID() == options::OPT_emit_pristine_llvm) { |
| 464 | // Translate -fcgl into -emit-llvm and -disable-llvm-passes. |
| 465 | DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_emit_llvm)); |
| 466 | DAL->AddFlagArg(BaseArg: nullptr, |
| 467 | Opt: Opts.getOption(Opt: options::OPT_disable_llvm_passes)); |
| 468 | A->claim(); |
| 469 | continue; |
| 470 | } |
| 471 | if (A->getOption().getID() == options::OPT_dxc_hlsl_version) { |
| 472 | // Translate -HV into -std for llvm |
| 473 | // depending on the value given |
| 474 | LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(Name: A->getValue()); |
| 475 | if (LangStd != LangStandard::lang_unspecified) { |
| 476 | LangStandard l = LangStandard::getLangStandardForKind(K: LangStd); |
| 477 | DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_std_EQ), |
| 478 | Value: l.getName()); |
| 479 | } else { |
| 480 | getDriver().Diag(DiagID: diag::err_drv_invalid_value) << "HV" << A->getValue(); |
| 481 | } |
| 482 | |
| 483 | A->claim(); |
| 484 | continue; |
| 485 | } |
| 486 | if (A->getOption().getID() == options::OPT_dxc_gis) { |
| 487 | // Translate -Gis into -ffp_model_EQ=strict |
| 488 | DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_ffp_model_EQ), |
| 489 | Value: "strict" ); |
| 490 | A->claim(); |
| 491 | continue; |
| 492 | } |
| 493 | if (A->getOption().getID() == options::OPT_fvk_use_dx_layout) { |
| 494 | // This is the only implemented layout so far. |
| 495 | A->claim(); |
| 496 | continue; |
| 497 | } |
| 498 | |
| 499 | if (A->getOption().getID() == options::OPT_fvk_use_scalar_layout) { |
| 500 | getDriver().Diag(DiagID: diag::err_drv_clang_unsupported) << A->getAsString(Args); |
| 501 | A->claim(); |
| 502 | continue; |
| 503 | } |
| 504 | |
| 505 | if (A->getOption().getID() == options::OPT_fvk_use_gl_layout) { |
| 506 | getDriver().Diag(DiagID: diag::err_drv_clang_unsupported) << A->getAsString(Args); |
| 507 | A->claim(); |
| 508 | continue; |
| 509 | } |
| 510 | |
| 511 | if (A->getOption().getID() == options::OPT_enable_16bit_types) { |
| 512 | // Translate -enable-16bit-types into -fnative-half-type and |
| 513 | // -fnative-int16-type |
| 514 | DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_fnative_half_type)); |
| 515 | DAL->AddFlagArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_fnative_int16_type)); |
| 516 | A->claim(); |
| 517 | continue; |
| 518 | } |
| 519 | if (A->getOption().getID() == options::OPT_dxc_col_major) { |
| 520 | DAL->AddJoinedArg(BaseArg: nullptr, |
| 521 | Opt: Opts.getOption(Opt: options::OPT_fmatrix_memory_layout_EQ), |
| 522 | Value: "column-major" ); |
| 523 | A->claim(); |
| 524 | continue; |
| 525 | } |
| 526 | if (A->getOption().getID() == options::OPT_dxc_row_major) { |
| 527 | DAL->AddJoinedArg(BaseArg: nullptr, |
| 528 | Opt: Opts.getOption(Opt: options::OPT_fmatrix_memory_layout_EQ), |
| 529 | Value: "row-major" ); |
| 530 | A->claim(); |
| 531 | continue; |
| 532 | } |
| 533 | |
| 534 | DAL->append(A); |
| 535 | } |
| 536 | |
| 537 | if (getArch() == llvm::Triple::spirv) { |
| 538 | std::vector<std::string> SpvExtensionArgs = |
| 539 | Args.getAllArgValues(Id: options::OPT_fspv_extension_EQ); |
| 540 | if (checkExtensionArgsAreValid(SpvExtensionArgs, Driver: getDriver())) { |
| 541 | SmallString<1024> LlvmOption = getSpirvExtArg(SpvExtensionArgs); |
| 542 | DAL->AddSeparateArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_mllvm), |
| 543 | Value: LlvmOption); |
| 544 | } |
| 545 | Args.claimAllArgs(Ids: options::OPT_fspv_extension_EQ); |
| 546 | } |
| 547 | |
| 548 | if (!DAL->hasArg(Ids: options::OPT_O_Group)) { |
| 549 | DAL->AddJoinedArg(BaseArg: nullptr, Opt: Opts.getOption(Opt: options::OPT_O), Value: "3" ); |
| 550 | } |
| 551 | |
| 552 | return DAL; |
| 553 | } |
| 554 | |
| 555 | bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const { |
| 556 | if (!Args.hasArg(Ids: options::OPT_dxc_Fo)) |
| 557 | return false; |
| 558 | |
| 559 | if (Args.getLastArg(Ids: options::OPT_dxc_disable_validation)) |
| 560 | return false; |
| 561 | |
| 562 | std::string DxvPath = GetProgramPath(Name: "dxv" ); |
| 563 | if (DxvPath != "dxv" ) |
| 564 | return true; |
| 565 | |
| 566 | getDriver().Diag(DiagID: diag::warn_drv_dxc_missing_dxv); |
| 567 | return false; |
| 568 | } |
| 569 | |
| 570 | bool HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const { |
| 571 | return Args.hasArg(Ids: options::OPT_metal) && Args.hasArg(Ids: options::OPT_dxc_Fo); |
| 572 | } |
| 573 | |
| 574 | bool HLSLToolChain::requiresObjcopy(DerivedArgList &Args) const { |
| 575 | return Args.hasArg(Ids: options::OPT_dxc_Fo) && |
| 576 | (Args.hasArg(Ids: options::OPT_dxc_strip_rootsignature) || |
| 577 | Args.hasArg(Ids: options::OPT_dxc_Frs) || isRootSignatureTarget(Args)); |
| 578 | } |
| 579 | |
| 580 | bool HLSLToolChain::isLastJob(DerivedArgList &Args, |
| 581 | Action::ActionClass AC) const { |
| 582 | // Note: we check in the reverse order of execution |
| 583 | if (requiresBinaryTranslation(Args)) |
| 584 | return AC == Action::Action::BinaryTranslatorJobClass; |
| 585 | if (requiresValidation(Args)) |
| 586 | return AC == Action::Action::BinaryAnalyzeJobClass; |
| 587 | if (requiresObjcopy(Args)) |
| 588 | return AC == Action::Action::ObjcopyJobClass; |
| 589 | |
| 590 | // No translation, validation, or objcopy are required, so this action must |
| 591 | // output to the result file. |
| 592 | return true; |
| 593 | } |
| 594 | |
| 595 | void HLSLToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { |
| 596 | CC1Args.push_back(Elt: "-Wconversion" ); |
| 597 | } |
| 598 | |