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 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 } 66 return false; 67 } 68 69 std::optional<std::string> tryParseProfile(StringRef Profile) { 70 // [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor] 71 SmallVector<StringRef, 3> Parts; 72 Profile.split(Parts, "_"); 73 if (Parts.size() != 3) 74 return std::nullopt; 75 76 Triple::EnvironmentType Kind = 77 StringSwitch<Triple::EnvironmentType>(Parts[0]) 78 .Case("ps", Triple::EnvironmentType::Pixel) 79 .Case("vs", Triple::EnvironmentType::Vertex) 80 .Case("gs", Triple::EnvironmentType::Geometry) 81 .Case("hs", Triple::EnvironmentType::Hull) 82 .Case("ds", Triple::EnvironmentType::Domain) 83 .Case("cs", Triple::EnvironmentType::Compute) 84 .Case("lib", Triple::EnvironmentType::Library) 85 .Case("ms", Triple::EnvironmentType::Mesh) 86 .Case("as", Triple::EnvironmentType::Amplification) 87 .Default(Triple::EnvironmentType::UnknownEnvironment); 88 if (Kind == Triple::EnvironmentType::UnknownEnvironment) 89 return std::nullopt; 90 91 unsigned long long Major = 0; 92 if (llvm::getAsUnsignedInteger(Parts[1], 0, Major)) 93 return std::nullopt; 94 95 unsigned long long Minor = 0; 96 if (Parts[2] == "x" && Kind == Triple::EnvironmentType::Library) 97 Minor = OfflineLibMinor; 98 else if (llvm::getAsUnsignedInteger(Parts[2], 0, Minor)) 99 return std::nullopt; 100 101 // Determine DXIL version using the minor version number of Shader 102 // Model version specified in target profile. Prior to decoupling DXIL version 103 // numbering from that of Shader Model DXIL version 1.Y corresponds to SM 6.Y. 104 // E.g., dxilv1.Y-unknown-shadermodelX.Y-hull 105 llvm::Triple T; 106 Triple::SubArchType SubArch = llvm::Triple::NoSubArch; 107 switch (Minor) { 108 case 0: 109 SubArch = llvm::Triple::DXILSubArch_v1_0; 110 break; 111 case 1: 112 SubArch = llvm::Triple::DXILSubArch_v1_1; 113 break; 114 case 2: 115 SubArch = llvm::Triple::DXILSubArch_v1_2; 116 break; 117 case 3: 118 SubArch = llvm::Triple::DXILSubArch_v1_3; 119 break; 120 case 4: 121 SubArch = llvm::Triple::DXILSubArch_v1_4; 122 break; 123 case 5: 124 SubArch = llvm::Triple::DXILSubArch_v1_5; 125 break; 126 case 6: 127 SubArch = llvm::Triple::DXILSubArch_v1_6; 128 break; 129 case 7: 130 SubArch = llvm::Triple::DXILSubArch_v1_7; 131 break; 132 case 8: 133 SubArch = llvm::Triple::DXILSubArch_v1_8; 134 break; 135 case OfflineLibMinor: 136 // Always consider minor version x as the latest supported DXIL version 137 SubArch = llvm::Triple::LatestDXILSubArch; 138 break; 139 default: 140 // No DXIL Version corresponding to specified Shader Model version found 141 return std::nullopt; 142 } 143 T.setArch(Triple::ArchType::dxil, SubArch); 144 T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() + 145 VersionTuple(Major, Minor).getAsString()); 146 T.setEnvironment(Kind); 147 if (isLegalShaderModel(T)) 148 return T.getTriple(); 149 else 150 return std::nullopt; 151 } 152 153 bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) { 154 VersionTuple Version; 155 if (Version.tryParse(ValVersionStr) || Version.getBuild() || 156 Version.getSubminor() || !Version.getMinor()) { 157 D.Diag(diag::err_drv_invalid_format_dxil_validator_version) 158 << ValVersionStr; 159 return false; 160 } 161 162 uint64_t Major = Version.getMajor(); 163 uint64_t Minor = *Version.getMinor(); 164 if (Major == 0 && Minor != 0) { 165 D.Diag(diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr; 166 return false; 167 } 168 VersionTuple MinVer(1, 0); 169 if (Version < MinVer) { 170 D.Diag(diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr; 171 return false; 172 } 173 return true; 174 } 175 176 } // namespace 177 178 void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA, 179 const InputInfo &Output, 180 const InputInfoList &Inputs, 181 const ArgList &Args, 182 const char *LinkingOutput) const { 183 std::string DxvPath = getToolChain().GetProgramPath("dxv"); 184 assert(DxvPath != "dxv" && "cannot find dxv"); 185 186 ArgStringList CmdArgs; 187 assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); 188 const InputInfo &Input = Inputs[0]; 189 assert(Input.isFilename() && "Unexpected verify input"); 190 // Grabbing the output of the earlier cc1 run. 191 CmdArgs.push_back(Input.getFilename()); 192 // Use the same name as output. 193 CmdArgs.push_back("-o"); 194 CmdArgs.push_back(Input.getFilename()); 195 196 const char *Exec = Args.MakeArgString(DxvPath); 197 C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(), 198 Exec, CmdArgs, Inputs, Input)); 199 } 200 201 /// DirectX Toolchain 202 HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple, 203 const ArgList &Args) 204 : ToolChain(D, Triple, Args) { 205 if (Args.hasArg(options::OPT_dxc_validator_path_EQ)) 206 getProgramPaths().push_back( 207 Args.getLastArgValue(options::OPT_dxc_validator_path_EQ).str()); 208 } 209 210 Tool *clang::driver::toolchains::HLSLToolChain::getTool( 211 Action::ActionClass AC) const { 212 switch (AC) { 213 case Action::BinaryAnalyzeJobClass: 214 if (!Validator) 215 Validator.reset(new tools::hlsl::Validator(*this)); 216 return Validator.get(); 217 default: 218 return ToolChain::getTool(AC); 219 } 220 } 221 222 std::optional<std::string> 223 clang::driver::toolchains::HLSLToolChain::parseTargetProfile( 224 StringRef TargetProfile) { 225 return tryParseProfile(TargetProfile); 226 } 227 228 DerivedArgList * 229 HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, 230 Action::OffloadKind DeviceOffloadKind) const { 231 DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs()); 232 233 const OptTable &Opts = getDriver().getOpts(); 234 235 for (Arg *A : Args) { 236 if (A->getOption().getID() == options::OPT_dxil_validator_version) { 237 StringRef ValVerStr = A->getValue(); 238 std::string ErrorMsg; 239 if (!isLegalValidatorVersion(ValVerStr, getDriver())) 240 continue; 241 } 242 if (A->getOption().getID() == options::OPT_dxc_entrypoint) { 243 DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_hlsl_entrypoint), 244 A->getValue()); 245 A->claim(); 246 continue; 247 } 248 if (A->getOption().getID() == options::OPT__SLASH_O) { 249 StringRef OStr = A->getValue(); 250 if (OStr == "d") { 251 DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_O0)); 252 A->claim(); 253 continue; 254 } else { 255 DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), OStr); 256 A->claim(); 257 continue; 258 } 259 } 260 if (A->getOption().getID() == options::OPT_emit_pristine_llvm) { 261 // Translate -fcgl into -emit-llvm and -disable-llvm-passes. 262 DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm)); 263 DAL->AddFlagArg(nullptr, 264 Opts.getOption(options::OPT_disable_llvm_passes)); 265 A->claim(); 266 continue; 267 } 268 if (A->getOption().getID() == options::OPT_dxc_hlsl_version) { 269 // Translate -HV into -std for llvm 270 // depending on the value given 271 LangStandard::Kind LangStd = LangStandard::getHLSLLangKind(A->getValue()); 272 if (LangStd != LangStandard::lang_unspecified) { 273 LangStandard l = LangStandard::getLangStandardForKind(LangStd); 274 DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_std_EQ), 275 l.getName()); 276 } else { 277 getDriver().Diag(diag::err_drv_invalid_value) << "HV" << A->getValue(); 278 } 279 280 A->claim(); 281 continue; 282 } 283 DAL->append(A); 284 } 285 286 // Add default validator version if not set. 287 // TODO: remove this once read validator version from validator. 288 if (!DAL->hasArg(options::OPT_dxil_validator_version)) { 289 const StringRef DefaultValidatorVer = "1.7"; 290 DAL->AddSeparateArg(nullptr, 291 Opts.getOption(options::OPT_dxil_validator_version), 292 DefaultValidatorVer); 293 } 294 if (!DAL->hasArg(options::OPT_O_Group)) { 295 DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), "3"); 296 } 297 298 return DAL; 299 } 300 301 bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const { 302 if (Args.getLastArg(options::OPT_dxc_disable_validation)) 303 return false; 304 305 std::string DxvPath = GetProgramPath("dxv"); 306 if (DxvPath != "dxv") 307 return true; 308 309 getDriver().Diag(diag::warn_drv_dxc_missing_dxv); 310 return false; 311 } 312