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 // dxil-unknown-shadermodel-hull 102 llvm::Triple T; 103 T.setArch(Triple::ArchType::dxil); 104 T.setOSName(Triple::getOSTypeName(Triple::OSType::ShaderModel).str() + 105 VersionTuple(Major, Minor).getAsString()); 106 T.setEnvironment(Kind); 107 if (isLegalShaderModel(T)) 108 return T.getTriple(); 109 else 110 return std::nullopt; 111 } 112 113 bool isLegalValidatorVersion(StringRef ValVersionStr, const Driver &D) { 114 VersionTuple Version; 115 if (Version.tryParse(ValVersionStr) || Version.getBuild() || 116 Version.getSubminor() || !Version.getMinor()) { 117 D.Diag(diag::err_drv_invalid_format_dxil_validator_version) 118 << ValVersionStr; 119 return false; 120 } 121 122 uint64_t Major = Version.getMajor(); 123 uint64_t Minor = *Version.getMinor(); 124 if (Major == 0 && Minor != 0) { 125 D.Diag(diag::err_drv_invalid_empty_dxil_validator_version) << ValVersionStr; 126 return false; 127 } 128 VersionTuple MinVer(1, 0); 129 if (Version < MinVer) { 130 D.Diag(diag::err_drv_invalid_range_dxil_validator_version) << ValVersionStr; 131 return false; 132 } 133 return true; 134 } 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 const char *LinkingOutput) const { 143 std::string DxvPath = getToolChain().GetProgramPath("dxv"); 144 assert(DxvPath != "dxv" && "cannot find dxv"); 145 146 ArgStringList CmdArgs; 147 assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); 148 const InputInfo &Input = Inputs[0]; 149 assert(Input.isFilename() && "Unexpected verify input"); 150 // Grabbing the output of the earlier cc1 run. 151 CmdArgs.push_back(Input.getFilename()); 152 // Use the same name as output. 153 CmdArgs.push_back("-o"); 154 CmdArgs.push_back(Input.getFilename()); 155 156 const char *Exec = Args.MakeArgString(DxvPath); 157 C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(), 158 Exec, CmdArgs, Inputs, Input)); 159 } 160 161 /// DirectX Toolchain 162 HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple, 163 const ArgList &Args) 164 : ToolChain(D, Triple, Args) { 165 if (Args.hasArg(options::OPT_dxc_validator_path_EQ)) 166 getProgramPaths().push_back( 167 Args.getLastArgValue(options::OPT_dxc_validator_path_EQ).str()); 168 } 169 170 Tool *clang::driver::toolchains::HLSLToolChain::getTool( 171 Action::ActionClass AC) const { 172 switch (AC) { 173 case Action::BinaryAnalyzeJobClass: 174 if (!Validator) 175 Validator.reset(new tools::hlsl::Validator(*this)); 176 return Validator.get(); 177 default: 178 return ToolChain::getTool(AC); 179 } 180 } 181 182 std::optional<std::string> 183 clang::driver::toolchains::HLSLToolChain::parseTargetProfile( 184 StringRef TargetProfile) { 185 return tryParseProfile(TargetProfile); 186 } 187 188 DerivedArgList * 189 HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch, 190 Action::OffloadKind DeviceOffloadKind) const { 191 DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs()); 192 193 const OptTable &Opts = getDriver().getOpts(); 194 195 for (Arg *A : Args) { 196 if (A->getOption().getID() == options::OPT_dxil_validator_version) { 197 StringRef ValVerStr = A->getValue(); 198 std::string ErrorMsg; 199 if (!isLegalValidatorVersion(ValVerStr, getDriver())) 200 continue; 201 } 202 if (A->getOption().getID() == options::OPT_dxc_entrypoint) { 203 DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_hlsl_entrypoint), 204 A->getValue()); 205 A->claim(); 206 continue; 207 } 208 if (A->getOption().getID() == options::OPT__SLASH_O) { 209 StringRef OStr = A->getValue(); 210 if (OStr == "d") { 211 DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_O0)); 212 A->claim(); 213 continue; 214 } else { 215 DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), OStr); 216 A->claim(); 217 continue; 218 } 219 } 220 if (A->getOption().getID() == options::OPT_emit_pristine_llvm) { 221 // Translate fcgl into -S -emit-llvm and -disable-llvm-passes. 222 DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_S)); 223 DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_llvm)); 224 DAL->AddFlagArg(nullptr, 225 Opts.getOption(options::OPT_disable_llvm_passes)); 226 A->claim(); 227 continue; 228 } 229 DAL->append(A); 230 } 231 232 if (DAL->hasArg(options::OPT_o)) { 233 // When run the whole pipeline. 234 if (!DAL->hasArg(options::OPT_emit_llvm)) 235 // Emit obj if write to file. 236 DAL->AddFlagArg(nullptr, Opts.getOption(options::OPT_emit_obj)); 237 } else 238 DAL->AddSeparateArg(nullptr, Opts.getOption(options::OPT_o), "-"); 239 240 // Add default validator version if not set. 241 // TODO: remove this once read validator version from validator. 242 if (!DAL->hasArg(options::OPT_dxil_validator_version)) { 243 const StringRef DefaultValidatorVer = "1.7"; 244 DAL->AddSeparateArg(nullptr, 245 Opts.getOption(options::OPT_dxil_validator_version), 246 DefaultValidatorVer); 247 } 248 if (!DAL->hasArg(options::OPT_O_Group)) { 249 DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_O), "3"); 250 } 251 // FIXME: add validation for enable_16bit_types should be after HLSL 2018 and 252 // shader model 6.2. 253 // See: https://github.com/llvm/llvm-project/issues/57876 254 return DAL; 255 } 256 257 bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const { 258 if (Args.getLastArg(options::OPT_dxc_disable_validation)) 259 return false; 260 261 std::string DxvPath = GetProgramPath("dxv"); 262 if (DxvPath != "dxv") 263 return true; 264 265 getDriver().Diag(diag::warn_drv_dxc_missing_dxv); 266 return false; 267 } 268