浏览代码

Add shader mask and min SM to RDAT

- add OP::GetMinShaderModelAndMask to compute mask and min SM
- account for ops that can be translated during linking
- update PS ops that could be allowed in library functions
- update min SM with newer optional feature flags
Tex Riddell 7 年之前
父节点
当前提交
ee550a4b53

+ 7 - 0
include/dxc/HLSL/DxilOperations.h

@@ -100,6 +100,13 @@ public:
   static bool IsDupDxilOpType(llvm::StructType *ST);
   static llvm::StructType *GetOriginalDxilOpType(llvm::StructType *ST,
                                                  llvm::Module &M);
+  static void GetMinShaderModelAndMask(OpCode C, bool bWithTranslation,
+                                       unsigned &major, unsigned &minor,
+                                       unsigned &mask);
+  static void ComputeMinShaderModelAndMask(const llvm::Function &function,
+                                           bool bWithTranslation,
+                                           unsigned &minMajor, unsigned &minMinor,
+                                           unsigned &shaderStageFlags);
 
 private:
   // Per-module properties.

+ 74 - 14
lib/HLSL/DxilContainerAssembler.cpp

@@ -929,18 +929,50 @@ private:
 
   std::vector<std::unique_ptr<RDATPart>> m_Parts;
   typedef llvm::SmallSetVector<uint32_t, 8> Indices;
-  typedef std::unordered_map<llvm::Function *, Indices> FunctionIndexMap;
+  typedef std::unordered_map<const llvm::Function *, Indices> FunctionIndexMap;
   FunctionIndexMap m_FuncToResNameOffset; // list of resources used
   FunctionIndexMap m_FuncToDependencies;  // list of unresolved functions used
 
-  llvm::Function *FindUsingFunction(llvm::Value *User) {
-    if (llvm::Instruction *I = dyn_cast<llvm::Instruction>(User)) {
+  struct ShaderCompatInfo {
+    ShaderCompatInfo()
+      : minMajor(6), minMinor(0),
+        mask(((unsigned)1 << (unsigned)DXIL::ShaderKind::Invalid) - 1)
+      {}
+    unsigned minMajor, minMinor, mask;
+  };
+  typedef std::unordered_map<const llvm::Function*, ShaderCompatInfo> FunctionShaderCompatMap;
+  FunctionShaderCompatMap m_FuncToShaderCompat;
+
+  void UpdateFunctionToShaderCompat(const llvm::Function* dxilFunc) {
+    for (const auto &user : dxilFunc->users()) {
+      if (const llvm::Instruction *I = dyn_cast<const llvm::Instruction>(user)) {
+        // Find calling function
+        const llvm::Function *F = cast<const llvm::Function>(I->getParent()->getParent());
+        // Insert or lookup info
+        ShaderCompatInfo &info = m_FuncToShaderCompat[F];
+        OpCode opcode = OP::GetDxilOpFuncCallInst(I);
+        unsigned major, minor, mask;
+        // bWithTranslation = true for library modules
+        OP::GetMinShaderModelAndMask(opcode, /*bWithTranslation*/true, major, minor, mask);
+        if (major > info.minMajor) {
+          info.minMajor = major;
+          info.minMinor = minor;
+        } else if (minor > info.minMinor) {
+          info.minMinor = minor;
+        }
+        info.mask &= mask;
+      }
+    }
+  }
+
+  const llvm::Function *FindUsingFunction(const llvm::Value *User) {
+    if (const llvm::Instruction *I = dyn_cast<const llvm::Instruction>(User)) {
       // Instruction should be inside a basic block, which is in a function
-      return cast<llvm::Function>(I->getParent()->getParent());
+      return cast<const llvm::Function>(I->getParent()->getParent());
     }
     // User can be either instruction, constant, or operator. But User is an
     // operator only if constant is a scalar value, not resource pointer.
-    llvm::Constant *CU = cast<llvm::Constant>(User);
+    const llvm::Constant *CU = cast<const llvm::Constant>(User);
     if (!CU->user_empty())
       return FindUsingFunction(*CU->user_begin());
     else
@@ -953,7 +985,7 @@ private:
     if (var) {
       for (auto user : var->users()) {
         // Find the function.
-        llvm::Function *F = FindUsingFunction(user);
+        const llvm::Function *F = FindUsingFunction(user);
         if (!F)
           continue;
         if (m_FuncToResNameOffset.find(F) == m_FuncToResNameOffset.end()) {
@@ -1020,7 +1052,7 @@ private:
 
   void UpdateFunctionDependency(llvm::Function *F, StringBufferPart &stringBufferPart) {
     for (const auto &user : F->users()) {
-      llvm::Function *userFunction = FindUsingFunction(user);
+      const llvm::Function *userFunction = FindUsingFunction(user);
       uint32_t index = stringBufferPart.Insert(F->getName());
       if (m_FuncToDependencies.find(userFunction) ==
           m_FuncToDependencies.end()) {
@@ -1032,18 +1064,19 @@ private:
   }
 
   void UpdateFunctionInfo(StringBufferPart &stringBufferPart) {
-    // TODO: get a list of valid shader flags
-    // TODO: get a minimum shader version
-    std::unordered_map<llvm::Function *, std::vector<StringRef>>
-        FuncToUnresolvedDependencies;
     m_Parts.emplace_back(std::make_unique<FunctionTable>());
     FunctionTable &functionTable = *reinterpret_cast<FunctionTable*>(m_Parts.back().get());
     m_Parts.emplace_back(std::make_unique<IndexArraysPart>());
     IndexArraysPart &indexArraysPart = *reinterpret_cast<IndexArraysPart*>(m_Parts.back().get());
     for (auto &function : m_Module.GetModule()->getFunctionList()) {
-      // If function is a declaration, it is an unresolved dependency in the library
-      if (function.isDeclaration() && !OP::IsDxilOpFunc(&function)) {
-        UpdateFunctionDependency(&function, stringBufferPart);
+      if (function.isDeclaration() && !function.isIntrinsic()) {
+        if (OP::IsDxilOpFunc(&function)) {
+          // update min shader model and shader stage mask per function
+          UpdateFunctionToShaderCompat(&function);
+        } else {
+          // collect unresolved dependencies per function
+          UpdateFunctionDependency(&function, stringBufferPart);
+        }
       }
     }
     for (auto &function : m_Module.GetModule()->getFunctionList()) {
@@ -1093,6 +1126,33 @@ private:
         uint64_t featureFlags = flags.GetFeatureInfo();
         info.FeatureInfo1 = featureFlags & 0xffffffff;
         info.FeatureInfo2 = (featureFlags >> 32) & 0xffffffff;
+        // Init min target 6.0
+        unsigned minMajor = 6, minMinor = 0;
+        // Increase min target based on feature flags:
+        if (flags.GetUseNativeLowPrecision() && flags.GetLowPrecisionPresent()) {
+          minMinor = 2;
+        } else if (flags.GetBarycentrics() || flags.GetViewID()) {
+          minMinor = 1;
+        }
+        if ((DXIL::ShaderKind)shaderKind == DXIL::ShaderKind::Library) {
+          // Init mask to all kinds for library functions
+          info.ShaderStageFlag = ((unsigned)1 << (unsigned)DXIL::ShaderKind::Invalid) - 1;
+        } else {
+          // Init mask to current kind for shader functions
+          info.ShaderStageFlag = (unsigned)1 << shaderKind;
+        }
+        auto it = m_FuncToShaderCompat.find(&function);
+        if (it != m_FuncToShaderCompat.end()) {
+          auto &compatInfo = it->second;
+          if (compatInfo.minMajor > minMajor) {
+            minMajor = compatInfo.minMajor;
+            minMinor = compatInfo.minMinor;
+          } else if (compatInfo.minMinor > minMinor) {
+            minMinor = compatInfo.minMinor;
+          }
+          info.ShaderStageFlag &= compatInfo.mask;
+        }
+        info.MinShaderTarget = EncodeVersion((DXIL::ShaderKind)shaderKind, minMajor, minMinor);
         functionTable.Insert(info);
       }
     }

+ 159 - 0
lib/HLSL/DxilOperations.cpp

@@ -509,6 +509,165 @@ bool OP::IsDxilOpGradient(OpCode C) {
   // OPCODE-GRADIENT:END
 }
 
+void OP::GetMinShaderModelAndMask(OpCode C, bool bWithTranslation,
+                                  unsigned &major, unsigned &minor,
+                                  unsigned &mask) {
+  unsigned op = (unsigned)C;
+  // Default is 6.0, all stages
+  major = 6;  minor = 0;
+  mask = ((unsigned)1 << (unsigned)DXIL::ShaderKind::Invalid) - 1;
+#define SFLAG(stage) ((unsigned)1 << (unsigned)DXIL::ShaderKind::stage)
+  /* <py::lines('OPCODE-SMMASK')>hctdb_instrhelp.get_min_sm_and_mask_text()</py>*/
+  // OPCODE-SMMASK:BEGIN
+  // Instructions: ThreadId=93, GroupId=94, ThreadIdInGroup=95,
+  // FlattenedThreadIdInGroup=96
+  if (93 <= op && op <= 96) {
+    mask = SFLAG(Compute);
+    return;
+  }
+  // Instructions: DomainLocation=105
+  if (op == 105) {
+    mask = SFLAG(Domain);
+    return;
+  }
+  // Instructions: LoadOutputControlPoint=103, LoadPatchConstant=104
+  if (103 <= op && op <= 104) {
+    mask = SFLAG(Domain) | SFLAG(Hull);
+    return;
+  }
+  // Instructions: EmitStream=97, CutStream=98, EmitThenCutStream=99,
+  // GSInstanceID=100
+  if (97 <= op && op <= 100) {
+    mask = SFLAG(Geometry);
+    return;
+  }
+  // Instructions: PrimitiveID=108
+  if (op == 108) {
+    mask = SFLAG(Geometry) | SFLAG(Domain) | SFLAG(Hull);
+    return;
+  }
+  // Instructions: StorePatchConstant=106, OutputControlPointID=107
+  if (106 <= op && op <= 107) {
+    mask = SFLAG(Hull);
+    return;
+  }
+  // Instructions: Sample=60, SampleBias=61, SampleCmp=64, CalculateLOD=81,
+  // DerivCoarseX=83, DerivCoarseY=84, DerivFineX=85, DerivFineY=86
+  if (60 <= op && op <= 61 || op == 64 || op == 81 || 83 <= op && op <= 86) {
+    mask = SFLAG(Library) | SFLAG(Pixel);
+    return;
+  }
+  // Instructions: RenderTargetGetSamplePosition=76,
+  // RenderTargetGetSampleCount=77, Discard=82, EvalSnapped=87,
+  // EvalSampleIndex=88, EvalCentroid=89, SampleIndex=90, Coverage=91,
+  // InnerCoverage=92
+  if (76 <= op && op <= 77 || op == 82 || 87 <= op && op <= 92) {
+    mask = SFLAG(Pixel);
+    return;
+  }
+  // Instructions: AttributeAtVertex=137
+  if (op == 137) {
+    major = 6;  minor = 1;
+    mask = SFLAG(Pixel);
+    return;
+  }
+  // Instructions: ViewID=138
+  if (op == 138) {
+    major = 6;  minor = 1;
+    mask = SFLAG(Vertex) | SFLAG(Hull) | SFLAG(Domain) | SFLAG(Geometry) | SFLAG(Pixel);
+    return;
+  }
+  // Instructions: RawBufferLoad=139, RawBufferStore=140
+  if (139 <= op && op <= 140) {
+    if (bWithTranslation) {
+      major = 6;  minor = 0;
+    } else {
+      major = 6;  minor = 2;
+    }
+    return;
+  }
+  // Instructions: IgnoreHit=155, AcceptHitAndEndSearch=156
+  if (155 <= op && op <= 156) {
+    major = 6;  minor = 3;
+    mask = SFLAG(Library) | SFLAG(AnyHit);
+    return;
+  }
+  // Instructions: CallShader=159
+  if (op == 159) {
+    major = 6;  minor = 3;
+    mask = SFLAG(Library) | SFLAG(ClosestHit) | SFLAG(RayGeneration) | SFLAG(Miss) | SFLAG(Callable);
+    return;
+  }
+  // Instructions: ReportHit=158
+  if (op == 158) {
+    major = 6;  minor = 3;
+    mask = SFLAG(Library) | SFLAG(Intersection);
+    return;
+  }
+  // Instructions: InstanceID=141, InstanceIndex=142, HitKind=143,
+  // ObjectRayOrigin=149, ObjectRayDirection=150, ObjectToWorld=151,
+  // WorldToObject=152, PrimitiveIndex=161
+  if (141 <= op && op <= 143 || 149 <= op && op <= 152 || op == 161) {
+    major = 6;  minor = 3;
+    mask = SFLAG(Library) | SFLAG(Intersection) | SFLAG(AnyHit) | SFLAG(ClosestHit);
+    return;
+  }
+  // Instructions: RayFlags=144, WorldRayOrigin=147, WorldRayDirection=148,
+  // RayTMin=153, RayTCurrent=154
+  if (op == 144 || 147 <= op && op <= 148 || 153 <= op && op <= 154) {
+    major = 6;  minor = 3;
+    mask = SFLAG(Library) | SFLAG(Intersection) | SFLAG(AnyHit) | SFLAG(ClosestHit) | SFLAG(Miss);
+    return;
+  }
+  // Instructions: TraceRay=157
+  if (op == 157) {
+    major = 6;  minor = 3;
+    mask = SFLAG(Library) | SFLAG(RayGeneration) | SFLAG(ClosestHit) | SFLAG(Miss);
+    return;
+  }
+  // Instructions: DispatchRaysIndex=145, DispatchRaysDimensions=146
+  if (145 <= op && op <= 146) {
+    major = 6;  minor = 3;
+    mask = SFLAG(Library) | SFLAG(RayGeneration) | SFLAG(Intersection) | SFLAG(AnyHit) | SFLAG(ClosestHit) | SFLAG(Miss) | SFLAG(Callable);
+    return;
+  }
+  // Instructions: CreateHandleForLib=160
+  if (op == 160) {
+    if (bWithTranslation) {
+      major = 6;  minor = 0;
+    } else {
+      major = 6;  minor = 3;
+    }
+    return;
+  }
+  // OPCODE-SMMASK:END
+#undef SFLAG
+}
+
+// This will only increase shader model and further restrict shader stages
+void OP::ComputeMinShaderModelAndMask(const llvm::Function &function,
+                                      bool bWithTranslation,
+                                      unsigned &minMajor, unsigned &minMinor,
+                                      unsigned &shaderStageFlags) {
+  for (const auto &Block : function) {
+    for (const auto &I : Block) {
+      if (IsDxilOpFuncCallInst(&I)) {
+        OpCode opcode = GetDxilOpFuncCallInst(&I);
+        unsigned major, minor, mask;
+        GetMinShaderModelAndMask(opcode, bWithTranslation, major, minor, mask);
+        if (major > minMajor) {
+          minMajor = major;
+          minMinor = minor;
+        }
+        else if (minor > minMinor) {
+          minMinor = minor;
+        }
+        shaderStageFlags &= mask;
+      }
+    }
+  }
+}
+
 static Type *GetOrCreateStructType(LLVMContext &Ctx, ArrayRef<Type*> types, StringRef Name, Module *pModule) {
   if (StructType *ST = pModule->getTypeByName(Name)) {
     // TODO: validate the exist type match types if needed.

+ 9 - 6
lib/HLSL/DxilValidation.cpp

@@ -613,12 +613,15 @@ static bool ValidateOpcodeInProfile(DXIL::OpCode opcode,
   // Instructions: StorePatchConstant=106, OutputControlPointID=107
   if (106 <= op && op <= 107)
     return (pSM->IsHS());
-  // Instructions: Sample=60, SampleBias=61, SampleCmp=64,
-  // RenderTargetGetSamplePosition=76, RenderTargetGetSampleCount=77,
-  // CalculateLOD=81, Discard=82, DerivCoarseX=83, DerivCoarseY=84,
-  // DerivFineX=85, DerivFineY=86, EvalSnapped=87, EvalSampleIndex=88,
-  // EvalCentroid=89, SampleIndex=90, Coverage=91, InnerCoverage=92
-  if (60 <= op && op <= 61 || op == 64 || 76 <= op && op <= 77 || 81 <= op && op <= 92)
+  // Instructions: Sample=60, SampleBias=61, SampleCmp=64, CalculateLOD=81,
+  // DerivCoarseX=83, DerivCoarseY=84, DerivFineX=85, DerivFineY=86
+  if (60 <= op && op <= 61 || op == 64 || op == 81 || 83 <= op && op <= 86)
+    return (pSM->IsLib() || pSM->IsPS());
+  // Instructions: RenderTargetGetSamplePosition=76,
+  // RenderTargetGetSampleCount=77, Discard=82, EvalSnapped=87,
+  // EvalSampleIndex=88, EvalCentroid=89, SampleIndex=90, Coverage=91,
+  // InnerCoverage=92
+  if (76 <= op && op <= 77 || op == 82 || 87 <= op && op <= 92)
     return (pSM->IsPS());
   // Instructions: AttributeAtVertex=137
   if (op == 137)

+ 10 - 2
utils/hct/hctdb.py

@@ -73,6 +73,7 @@ class db_dxil_inst(object):
             setattr(self, k, v)
         self.is_dxil_op = self.dxil_op != "" # whether this is a DXIL operation
         self.is_reserved = self.dxil_class == "Reserved"
+        self.shader_model_translated = None # minimum shader model required with translation by linker
 
     def __str__(self):
         return self.name
@@ -268,13 +269,18 @@ class db_dxil(object):
             self.name_idx[i].category = "Resources"
         for i in "Sample,SampleBias,SampleLevel,SampleGrad,SampleCmp,SampleCmpLevelZero,Texture2DMSGetSamplePosition,RenderTargetGetSamplePosition,RenderTargetGetSampleCount".split(","):
             self.name_idx[i].category = "Resources - sample"
-        for i in "Sample,SampleBias,SampleCmp,RenderTargetGetSamplePosition,RenderTargetGetSampleCount".split(","):
+        for i in "Sample,SampleBias,SampleCmp".split(","):
+            self.name_idx[i].shader_stages = ("library", "pixel")
+        for i in "RenderTargetGetSamplePosition,RenderTargetGetSampleCount".split(","):
             self.name_idx[i].shader_stages = ("pixel",)
         for i in "TextureGather,TextureGatherCmp".split(","):
             self.name_idx[i].category = "Resources - gather"
         for i in "AtomicBinOp,AtomicCompareExchange,Barrier".split(","):
             self.name_idx[i].category = "Synchronization"
-        for i in "CalculateLOD,Discard,DerivCoarseX,DerivCoarseY,DerivFineX,DerivFineY,EvalSnapped,EvalSampleIndex,EvalCentroid,SampleIndex,Coverage,InnerCoverage,AttributeAtVertex".split(","):
+        for i in "CalculateLOD,DerivCoarseX,DerivCoarseY,DerivFineX,DerivFineY".split(","):
+            self.name_idx[i].category = "Pixel shader"
+            self.name_idx[i].shader_stages = ("library", "pixel")
+        for i in "Discard,EvalSnapped,EvalSampleIndex,EvalCentroid,SampleIndex,Coverage,InnerCoverage,AttributeAtVertex".split(","):
             self.name_idx[i].category = "Pixel shader"
             self.name_idx[i].shader_stages = ("pixel",)
         for i in "ThreadId,GroupId,ThreadIdInGroup,FlattenedThreadIdInGroup".split(","):
@@ -314,6 +320,7 @@ class db_dxil(object):
             self.name_idx[i].shader_model = 6,1
         for i in "RawBufferLoad,RawBufferStore".split(","):
             self.name_idx[i].shader_model = 6,2
+            self.name_idx[i].shader_model_translated = 6,0
         for i in "DispatchRaysIndex,DispatchRaysDimensions".split(","):
             self.name_idx[i].category = "Ray Dispatch Arguments"
             self.name_idx[i].shader_model = 6,3
@@ -365,6 +372,7 @@ class db_dxil(object):
         for i in "CreateHandleForLib".split(","):
             self.name_idx[i].category = "Library create handle from resource struct (like HL intrinsic)"
             self.name_idx[i].shader_model = 6,3
+            self.name_idx[i].shader_model_translated = 6,0
 
     def populate_llvm_instructions(self):
         # Add instructions that map to LLVM instructions.

+ 61 - 0
utils/hct/hctdb_instrhelp.py

@@ -921,6 +921,67 @@ def get_opsigs():
     code += "};\n"
     return code
 
+shader_stage_to_ShaderKind = {
+    'vertex': 'Vertex',
+    'pixel': 'Pixel',
+    'geometry': 'Geometry',
+    'compute': 'Compute',
+    'hull': 'Hull',
+    'domain': 'Domain',
+    'library': 'Library',
+    'raygeneration': 'RayGeneration',
+    'intersection': 'Intersection',
+    'anyhit': 'AnyHit',
+    'closesthit': 'ClosestHit',
+    'miss': 'Miss',
+    'callable': 'Callable',
+}
+
+def get_min_sm_and_mask_text():
+    db = get_db_dxil()
+    instrs = [i for i in db.instr if i.is_dxil_op]
+    instrs = sorted(instrs, key=lambda v : (v.shader_model, v.shader_model_translated, v.shader_stages, v.dxil_opid))
+    last_model = None
+    last_model_translated = None
+    last_stage = None
+    grouped_instrs = []
+    code = ""
+    def flush_instrs(grouped_instrs, last_model, last_model_translated, last_stage):
+        if len(grouped_instrs) == 0:
+            return ""
+        result = format_comment("// ", "Instructions: %s" % ", ".join([i.name + "=" + str(i.dxil_opid) for i in grouped_instrs]))
+        result += "if (" + build_range_code("op", [i.dxil_opid for i in grouped_instrs]) + ") {\n"
+        default = True
+        if last_model != (6,0):
+            default = False
+            if last_model_translated:
+                result += "  if (bWithTranslation) {\n"
+                result += "    major = %d;  minor = %d;\n  } else {\n  " % last_model_translated
+            result += "  major = %d;  minor = %d;\n" % last_model
+            if last_model_translated:
+                result += "  }\n"
+        if last_stage:
+            default = False
+            result += "  mask = %s;\n" % ' | '.join([   'SFLAG(%s)' % shader_stage_to_ShaderKind[c]
+                                                        for c in last_stage
+                                                        ])
+        if default:
+            # don't write these out, instead fall through
+            return ""
+        return result + "  return;\n}\n"
+
+    for i in instrs:
+        if ((i.shader_model, i.shader_model_translated, i.shader_stages) !=
+                (last_model, last_model_translated, last_stage)):
+            code += flush_instrs(grouped_instrs, last_model, last_model_translated, last_stage)
+            grouped_instrs = []
+            last_model = i.shader_model
+            last_model_translated = i.shader_model_translated
+            last_stage = i.shader_stages
+        grouped_instrs.append(i)
+    code += flush_instrs(grouped_instrs, last_model, last_model_translated, last_stage)
+    return code
+
 check_pSM_for_shader_stage = {
     'vertex': 'pSM->IsVS()',
     'pixel': 'pSM->IsPS()',