449 lines · cpp
1//===- DXILShaderFlags.cpp - DXIL Shader Flags helper objects -------------===//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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file This file contains helper objects and APIs for working with DXIL10/// Shader Flags.11///12//===----------------------------------------------------------------------===//13 14#include "DXILShaderFlags.h"15#include "DirectX.h"16#include "llvm/ADT/SCCIterator.h"17#include "llvm/ADT/SmallVector.h"18#include "llvm/Analysis/CallGraph.h"19#include "llvm/Analysis/DXILResource.h"20#include "llvm/IR/Attributes.h"21#include "llvm/IR/DiagnosticInfo.h"22#include "llvm/IR/Instruction.h"23#include "llvm/IR/Instructions.h"24#include "llvm/IR/IntrinsicInst.h"25#include "llvm/IR/Intrinsics.h"26#include "llvm/IR/IntrinsicsDirectX.h"27#include "llvm/IR/Module.h"28#include "llvm/InitializePasses.h"29#include "llvm/Support/FormatVariadic.h"30#include "llvm/Support/raw_ostream.h"31 32using namespace llvm;33using namespace llvm::dxil;34 35static bool hasUAVsAtEveryStage(const DXILResourceMap &DRM,36 const ModuleMetadataInfo &MMDI) {37 if (DRM.uavs().empty())38 return false;39 40 switch (MMDI.ShaderProfile) {41 default:42 return false;43 case Triple::EnvironmentType::Compute:44 case Triple::EnvironmentType::Pixel:45 return false;46 case Triple::EnvironmentType::Vertex:47 case Triple::EnvironmentType::Geometry:48 case Triple::EnvironmentType::Hull:49 case Triple::EnvironmentType::Domain:50 return true;51 case Triple::EnvironmentType::Library:52 case Triple::EnvironmentType::RayGeneration:53 case Triple::EnvironmentType::Intersection:54 case Triple::EnvironmentType::AnyHit:55 case Triple::EnvironmentType::ClosestHit:56 case Triple::EnvironmentType::Miss:57 case Triple::EnvironmentType::Callable:58 case Triple::EnvironmentType::Mesh:59 case Triple::EnvironmentType::Amplification:60 return MMDI.ValidatorVersion < VersionTuple(1, 8);61 }62}63 64static bool checkWaveOps(Intrinsic::ID IID) {65 // Currently unsupported intrinsics66 // case Intrinsic::dx_wave_getlanecount:67 // case Intrinsic::dx_wave_allequal:68 // case Intrinsic::dx_wave_ballot:69 // case Intrinsic::dx_wave_readfirst:70 // case Intrinsic::dx_wave_reduce.and:71 // case Intrinsic::dx_wave_reduce.or:72 // case Intrinsic::dx_wave_reduce.xor:73 // case Intrinsic::dx_wave_prefixop:74 // case Intrinsic::dx_quad.readat:75 // case Intrinsic::dx_quad.readacrossx:76 // case Intrinsic::dx_quad.readacrossy:77 // case Intrinsic::dx_quad.readacrossdiagonal:78 // case Intrinsic::dx_wave_prefixballot:79 // case Intrinsic::dx_wave_match:80 // case Intrinsic::dx_wavemulti.*:81 // case Intrinsic::dx_wavemulti.ballot:82 // case Intrinsic::dx_quad.vote:83 switch (IID) {84 default:85 return false;86 case Intrinsic::dx_wave_is_first_lane:87 case Intrinsic::dx_wave_getlaneindex:88 case Intrinsic::dx_wave_any:89 case Intrinsic::dx_wave_all:90 case Intrinsic::dx_wave_readlane:91 case Intrinsic::dx_wave_active_countbits:92 // Wave Active Op Variants93 case Intrinsic::dx_wave_reduce_sum:94 case Intrinsic::dx_wave_reduce_usum:95 case Intrinsic::dx_wave_reduce_max:96 case Intrinsic::dx_wave_reduce_umax:97 case Intrinsic::dx_wave_reduce_min:98 case Intrinsic::dx_wave_reduce_umin:99 return true;100 }101}102 103// Checks to see if the status bit from a load with status104// instruction is ever extracted. If it is, the module needs105// to have the TiledResources shader flag set.106bool checkIfStatusIsExtracted(const IntrinsicInst &II) {107 [[maybe_unused]] Intrinsic::ID IID = II.getIntrinsicID();108 assert(IID == Intrinsic::dx_resource_load_typedbuffer ||109 IID == Intrinsic::dx_resource_load_rawbuffer &&110 "unexpected intrinsic ID");111 for (const User *U : II.users()) {112 if (const ExtractValueInst *EVI = dyn_cast<ExtractValueInst>(U)) {113 // Resource load operations return a {result, status} pair.114 // Check if we extract the status115 if (EVI->getNumIndices() == 1 && EVI->getIndices()[0] == 1)116 return true;117 }118 }119 120 return false;121}122 123/// Update the shader flags mask based on the given instruction.124/// \param CSF Shader flags mask to update.125/// \param I Instruction to check.126void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF,127 const Instruction &I,128 DXILResourceTypeMap &DRTM,129 const ModuleMetadataInfo &MMDI) {130 if (!CSF.Doubles)131 CSF.Doubles = I.getType()->getScalarType()->isDoubleTy();132 133 if (!CSF.Doubles) {134 for (const Value *Op : I.operands()) {135 if (Op->getType()->getScalarType()->isDoubleTy()) {136 CSF.Doubles = true;137 break;138 }139 }140 }141 142 if (CSF.Doubles) {143 switch (I.getOpcode()) {144 case Instruction::FDiv:145 case Instruction::UIToFP:146 case Instruction::SIToFP:147 case Instruction::FPToUI:148 case Instruction::FPToSI:149 CSF.DX11_1_DoubleExtensions = true;150 break;151 }152 }153 154 if (!CSF.LowPrecisionPresent)155 CSF.LowPrecisionPresent = I.getType()->getScalarType()->isIntegerTy(16) ||156 I.getType()->getScalarType()->isHalfTy();157 158 if (!CSF.LowPrecisionPresent) {159 for (const Value *Op : I.operands()) {160 if (Op->getType()->getScalarType()->isIntegerTy(16) ||161 Op->getType()->getScalarType()->isHalfTy()) {162 CSF.LowPrecisionPresent = true;163 break;164 }165 }166 }167 168 if (CSF.LowPrecisionPresent) {169 if (CSF.NativeLowPrecisionMode)170 CSF.NativeLowPrecision = true;171 else172 CSF.MinimumPrecision = true;173 }174 175 if (!CSF.Int64Ops)176 CSF.Int64Ops = I.getType()->getScalarType()->isIntegerTy(64);177 178 if (!CSF.Int64Ops && !isa<LifetimeIntrinsic>(&I)) {179 for (const Value *Op : I.operands()) {180 if (Op->getType()->getScalarType()->isIntegerTy(64)) {181 CSF.Int64Ops = true;182 break;183 }184 }185 }186 187 if (const auto *II = dyn_cast<IntrinsicInst>(&I)) {188 switch (II->getIntrinsicID()) {189 default:190 break;191 case Intrinsic::dx_resource_handlefrombinding: {192 dxil::ResourceTypeInfo &RTI = DRTM[cast<TargetExtType>(II->getType())];193 194 // Set ResMayNotAlias if DXIL validator version >= 1.8 and the function195 // uses UAVs196 if (!CSF.ResMayNotAlias && CanSetResMayNotAlias &&197 MMDI.ValidatorVersion >= VersionTuple(1, 8) && RTI.isUAV())198 CSF.ResMayNotAlias = true;199 200 switch (RTI.getResourceKind()) {201 case dxil::ResourceKind::StructuredBuffer:202 case dxil::ResourceKind::RawBuffer:203 CSF.EnableRawAndStructuredBuffers = true;204 break;205 default:206 break;207 }208 break;209 }210 case Intrinsic::dx_resource_load_typedbuffer: {211 dxil::ResourceTypeInfo &RTI =212 DRTM[cast<TargetExtType>(II->getArgOperand(0)->getType())];213 if (RTI.isTyped())214 CSF.TypedUAVLoadAdditionalFormats |= RTI.getTyped().ElementCount > 1;215 if (!CSF.TiledResources && checkIfStatusIsExtracted(*II))216 CSF.TiledResources = true;217 break;218 }219 case Intrinsic::dx_resource_load_rawbuffer: {220 if (!CSF.TiledResources && checkIfStatusIsExtracted(*II))221 CSF.TiledResources = true;222 break;223 }224 }225 }226 // Handle call instructions227 if (auto *CI = dyn_cast<CallInst>(&I)) {228 const Function *CF = CI->getCalledFunction();229 // Merge-in shader flags mask of the called function in the current module230 if (FunctionFlags.contains(CF))231 CSF.merge(FunctionFlags[CF]);232 233 // TODO: Set DX11_1_DoubleExtensions if I is a call to DXIL intrinsic234 // DXIL::Opcode::Fma https://github.com/llvm/llvm-project/issues/114554235 236 CSF.WaveOps |= checkWaveOps(CI->getIntrinsicID());237 }238}239 240/// Set shader flags that apply to all functions within the module241ComputedShaderFlags242ModuleShaderFlags::gatherGlobalModuleFlags(const Module &M,243 const DXILResourceMap &DRM,244 const ModuleMetadataInfo &MMDI) {245 246 ComputedShaderFlags CSF;247 248 // Set DisableOptimizations flag based on the presence of OptimizeNone249 // attribute of entry functions.250 if (MMDI.EntryPropertyVec.size() > 0) {251 CSF.DisableOptimizations = MMDI.EntryPropertyVec[0].Entry->hasFnAttribute(252 llvm::Attribute::OptimizeNone);253 // Ensure all entry functions have the same optimization attribute254 for (const auto &EntryFunProps : MMDI.EntryPropertyVec)255 if (CSF.DisableOptimizations !=256 EntryFunProps.Entry->hasFnAttribute(llvm::Attribute::OptimizeNone))257 EntryFunProps.Entry->getContext().diagnose(DiagnosticInfoUnsupported(258 *(EntryFunProps.Entry), "Inconsistent optnone attribute "));259 }260 261 CSF.UAVsAtEveryStage = hasUAVsAtEveryStage(DRM, MMDI);262 263 // Set the Max64UAVs flag if the number of UAVs is > 8264 uint32_t NumUAVs = 0;265 for (auto &UAV : DRM.uavs())266 if (MMDI.ValidatorVersion < VersionTuple(1, 6))267 NumUAVs++;268 else // MMDI.ValidatorVersion >= VersionTuple(1, 6)269 NumUAVs += UAV.getBinding().Size;270 if (NumUAVs > 8)271 CSF.Max64UAVs = true;272 273 // Set the module flag that enables native low-precision execution mode.274 // NativeLowPrecisionMode can only be set when the command line option275 // -enable-16bit-types is provided. This is indicated by the dx.nativelowprec276 // module flag being set277 // This flag is needed even if the module does not use 16-bit types because a278 // corresponding debug module may include 16-bit types, and tools that use the279 // debug module may expect it to have the same flags as the original280 if (auto *NativeLowPrec = mdconst::extract_or_null<ConstantInt>(281 M.getModuleFlag("dx.nativelowprec")))282 if (MMDI.ShaderModelVersion >= VersionTuple(6, 2))283 CSF.NativeLowPrecisionMode = NativeLowPrec->getValue().getBoolValue();284 285 // Set ResMayNotAlias to true if DXIL validator version < 1.8 and there286 // are UAVs present globally.287 if (CanSetResMayNotAlias && MMDI.ValidatorVersion < VersionTuple(1, 8))288 CSF.ResMayNotAlias = !DRM.uavs().empty();289 290 return CSF;291}292 293/// Construct ModuleShaderFlags for module Module M294void ModuleShaderFlags::initialize(Module &M, DXILResourceTypeMap &DRTM,295 const DXILResourceMap &DRM,296 const ModuleMetadataInfo &MMDI) {297 298 CanSetResMayNotAlias = MMDI.DXILVersion >= VersionTuple(1, 7);299 // The command line option -res-may-alias will set the dx.resmayalias module300 // flag to 1, thereby disabling the ability to set the ResMayNotAlias flag301 if (auto *ResMayAlias = mdconst::extract_or_null<ConstantInt>(302 M.getModuleFlag("dx.resmayalias")))303 if (ResMayAlias->getValue().getBoolValue())304 CanSetResMayNotAlias = false;305 306 ComputedShaderFlags GlobalSFMask = gatherGlobalModuleFlags(M, DRM, MMDI);307 308 CallGraph CG(M);309 310 // Compute Shader Flags Mask for all functions using post-order visit of SCC311 // of the call graph.312 for (scc_iterator<CallGraph *> SCCI = scc_begin(&CG); !SCCI.isAtEnd();313 ++SCCI) {314 const std::vector<CallGraphNode *> &CurSCC = *SCCI;315 316 // Union of shader masks of all functions in CurSCC317 ComputedShaderFlags SCCSF;318 // List of functions in CurSCC that are neither external nor declarations319 // and hence whose flags are collected320 SmallVector<Function *> CurSCCFuncs;321 for (CallGraphNode *CGN : CurSCC) {322 Function *F = CGN->getFunction();323 if (!F)324 continue;325 326 if (F->isDeclaration()) {327 assert(!F->getName().starts_with("dx.op.") &&328 "DXIL Shader Flag analysis should not be run post-lowering.");329 continue;330 }331 332 ComputedShaderFlags CSF = GlobalSFMask;333 for (const auto &BB : *F)334 for (const auto &I : BB)335 updateFunctionFlags(CSF, I, DRTM, MMDI);336 // Update combined shader flags mask for all functions in this SCC337 SCCSF.merge(CSF);338 339 CurSCCFuncs.push_back(F);340 }341 342 // Update combined shader flags mask for all functions of the module343 CombinedSFMask.merge(SCCSF);344 345 // Shader flags mask of each of the functions in an SCC of the call graph is346 // the union of all functions in the SCC. Update shader flags masks of347 // functions in CurSCC accordingly. This is trivially true if SCC contains348 // one function.349 for (Function *F : CurSCCFuncs)350 // Merge SCCSF with that of F351 FunctionFlags[F].merge(SCCSF);352 }353}354 355void ComputedShaderFlags::print(raw_ostream &OS) const {356 uint64_t FlagVal = (uint64_t) * this;357 OS << formatv("; Shader Flags Value: {0:x8}\n;\n", FlagVal);358 if (FlagVal == 0)359 return;360 OS << "; Note: shader requires additional functionality:\n";361#define SHADER_FEATURE_FLAG(FeatureBit, DxilModuleNum, FlagName, Str) \362 if (FlagName) \363 (OS << ";").indent(7) << Str << "\n";364#include "llvm/BinaryFormat/DXContainerConstants.def"365 OS << "; Note: extra DXIL module flags:\n";366#define DXIL_MODULE_FLAG(DxilModuleBit, FlagName, Str) \367 if (FlagName) \368 (OS << ";").indent(7) << Str << "\n";369#include "llvm/BinaryFormat/DXContainerConstants.def"370 OS << ";\n";371}372 373/// Return the shader flags mask of the specified function Func.374const ComputedShaderFlags &375ModuleShaderFlags::getFunctionFlags(const Function *Func) const {376 auto Iter = FunctionFlags.find(Func);377 assert((Iter != FunctionFlags.end() && Iter->first == Func) &&378 "Get Shader Flags : No Shader Flags Mask exists for function");379 return Iter->second;380}381 382//===----------------------------------------------------------------------===//383// ShaderFlagsAnalysis and ShaderFlagsAnalysisPrinterPass384 385// Provide an explicit template instantiation for the static ID.386AnalysisKey ShaderFlagsAnalysis::Key;387 388ModuleShaderFlags ShaderFlagsAnalysis::run(Module &M,389 ModuleAnalysisManager &AM) {390 DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);391 DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);392 const ModuleMetadataInfo MMDI = AM.getResult<DXILMetadataAnalysis>(M);393 394 ModuleShaderFlags MSFI;395 MSFI.initialize(M, DRTM, DRM, MMDI);396 397 return MSFI;398}399 400PreservedAnalyses ShaderFlagsAnalysisPrinter::run(Module &M,401 ModuleAnalysisManager &AM) {402 const ModuleShaderFlags &FlagsInfo = AM.getResult<ShaderFlagsAnalysis>(M);403 // Print description of combined shader flags for all module functions404 OS << "; Combined Shader Flags for Module\n";405 FlagsInfo.getCombinedFlags().print(OS);406 // Print shader flags mask for each of the module functions407 OS << "; Shader Flags for Module Functions\n";408 for (const auto &F : M.getFunctionList()) {409 if (F.isDeclaration())410 continue;411 const ComputedShaderFlags &SFMask = FlagsInfo.getFunctionFlags(&F);412 OS << formatv("; Function {0} : {1:x8}\n;\n", F.getName(),413 (uint64_t)(SFMask));414 }415 416 return PreservedAnalyses::all();417}418 419//===----------------------------------------------------------------------===//420// ShaderFlagsAnalysis and ShaderFlagsAnalysisPrinterPass421 422bool ShaderFlagsAnalysisWrapper::runOnModule(Module &M) {423 DXILResourceTypeMap &DRTM =424 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();425 DXILResourceMap &DRM =426 getAnalysis<DXILResourceWrapperPass>().getResourceMap();427 const ModuleMetadataInfo MMDI =428 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();429 430 MSFI.initialize(M, DRTM, DRM, MMDI);431 return false;432}433 434void ShaderFlagsAnalysisWrapper::getAnalysisUsage(AnalysisUsage &AU) const {435 AU.setPreservesAll();436 AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();437 AU.addRequiredTransitive<DXILResourceWrapperPass>();438 AU.addRequired<DXILMetadataAnalysisWrapperPass>();439}440 441char ShaderFlagsAnalysisWrapper::ID = 0;442 443INITIALIZE_PASS_BEGIN(ShaderFlagsAnalysisWrapper, "dx-shader-flag-analysis",444 "DXIL Shader Flag Analysis", true, true)445INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)446INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)447INITIALIZE_PASS_END(ShaderFlagsAnalysisWrapper, "dx-shader-flag-analysis",448 "DXIL Shader Flag Analysis", true, true)449