Bläddra i källkod

Allow optimizations to eliminate phis on undef resources before considering them errors

Some large shaders exhibit a behavior where phi nodes are created for resources, in which one of the possible incoming values is undef. This gets cleaned up later such that there are no undef resources left. However the fail-undef-resources pass has already failed compilation by that point. The fix is to change that pass to invalidate-undef-resources and replace the undefs with a special invalid value, such that we can produce an error later if still necessary, when optimization passes have been run such that temporary undef resources have been eliminated.
Tristan Labelle 6 år sedan
förälder
incheckning
3527762fd0

+ 2 - 2
include/dxc/HLSL/DxilGenerationPass.h

@@ -67,7 +67,7 @@ ModulePass *createDxilLegalizeResources();
 ModulePass *createDxilLegalizeEvalOperationsPass();
 FunctionPass *createDxilLegalizeSampleOffsetPass();
 FunctionPass *createDxilSimpleGVNHoistPass();
-ModulePass *createFailUndefResourcePass();
+ModulePass *createInvalidateUndefResourcesPass();
 FunctionPass *createSimplifyInstPass();
 ModulePass *createDxilTranslateRawBuffer();
 ModulePass *createNoPausePassesPass();
@@ -99,7 +99,7 @@ void initializeDxilLegalizeResourcesPass(llvm::PassRegistry&);
 void initializeDxilLegalizeEvalOperationsPass(llvm::PassRegistry&);
 void initializeDxilLegalizeSampleOffsetPassPass(llvm::PassRegistry&);
 void initializeDxilSimpleGVNHoistPass(llvm::PassRegistry&);
-void initializeFailUndefResourcePass(llvm::PassRegistry&);
+void initializeInvalidateUndefResourcesPass(llvm::PassRegistry&);
 void initializeSimplifyInstPass(llvm::PassRegistry&);
 void initializeDxilTranslateRawBufferPass(llvm::PassRegistry&);
 void initializeNoPausePassesPass(llvm::PassRegistry&);

+ 1 - 1
lib/HLSL/DxcOptimizer.cpp

@@ -110,7 +110,7 @@ HRESULT SetupRegistryPassForHLSL() {
     initializeDynamicIndexingVectorToArrayPass(Registry);
     initializeEarlyCSELegacyPassPass(Registry);
     initializeEliminateAvailableExternallyPass(Registry);
-    initializeFailUndefResourcePass(Registry);
+    initializeInvalidateUndefResourcesPass(Registry);
     initializeFloat2IntPass(Registry);
     initializeFunctionAttrsPass(Registry);
     initializeGVNPass(Registry);

+ 20 - 0
lib/HLSL/DxilCondenseResources.cpp

@@ -447,6 +447,8 @@ public:
     m_bIsLib = DM.GetShaderModel()->IsLib();
     m_bLegalizationFailed = false;
 
+    FailOnPoisonResources();
+
     bool bChanged = false;
     unsigned numResources = DM.GetCBuffers().size() + DM.GetUAVs().size() +
                             DM.GetSRVs().size() + DM.GetSamplers().size();
@@ -506,6 +508,7 @@ public:
   }
 
 private:
+  void FailOnPoisonResources();
   bool RemovePhiOnResource();
   void UpdateResourceSymbols();
   void TranslateDxilResourceUses(DxilResourceBase &res);
@@ -1694,6 +1697,23 @@ void UpdateStructTypeForLegacyLayoutOnDM(DxilModule &DM) {
 
 } // namespace
 
+void DxilLowerCreateHandleForLib::FailOnPoisonResources() {
+  // A previous pass replaced all undef resources with constant zero resources.
+  // If those made it here, the program is malformed.
+  for (Function &Func : this->m_DM->GetModule()->functions()) {
+    hlsl::OP::OpCodeClass OpcodeClass;
+    if (m_DM->GetOP()->GetOpCodeClass(&Func, OpcodeClass)
+      && OpcodeClass == OP::OpCodeClass::CreateHandleForLib) {
+      Type *ResTy = Func.getFunctionType()->getParamType(
+        DXIL::OperandIndex::kCreateHandleForLibResOpIdx);
+      Constant *PoisonRes = ConstantAggregateZero::get(ResTy);
+      for (User *PoisonUser : PoisonRes->users())
+        if (Instruction *PoisonUserInst = dyn_cast<Instruction>(PoisonUser))
+          dxilutil::EmitResMappingError(PoisonUserInst);
+    }
+  }
+}
+
 void DxilLowerCreateHandleForLib::UpdateStructTypeForLegacyLayout() {
   UpdateStructTypeForLegacyLayoutOnDM(*m_DM);
 }

+ 19 - 15
lib/HLSL/DxilPreparePasses.cpp

@@ -36,40 +36,44 @@ using namespace llvm;
 using namespace hlsl;
 
 namespace {
-class FailUndefResource : public ModulePass {
+class InvalidateUndefResources : public ModulePass {
 public:
   static char ID;
 
-  explicit FailUndefResource() : ModulePass(ID) {
+  explicit InvalidateUndefResources() : ModulePass(ID) {
     initializeScalarizerPass(*PassRegistry::getPassRegistry());
   }
 
-  const char *getPassName() const override { return "Fail on undef resource use"; }
+  const char *getPassName() const override { return "Invalidate undef resources"; }
 
   bool runOnModule(Module &M) override;
 };
 }
 
-char FailUndefResource::ID = 0;
+char InvalidateUndefResources::ID = 0;
 
-ModulePass *llvm::createFailUndefResourcePass() { return new FailUndefResource(); }
+ModulePass *llvm::createInvalidateUndefResourcesPass() { return new InvalidateUndefResources(); }
 
-INITIALIZE_PASS(FailUndefResource, "fail-undef-resource", "Fail on undef resource use", false, false)
+INITIALIZE_PASS(InvalidateUndefResources, "invalidate-undef-resource", "Invalidate undef resources", false, false)
 
-bool FailUndefResource::runOnModule(Module &M) {
-  // Undef resources may be removed on simplify due to the interpretation
-  // of undef that any value could be substituted for identical meaning.
-  // However, these likely indicate uninitialized locals being used in
-  // some code path, which we should catch and report.
+bool InvalidateUndefResources::runOnModule(Module &M) {
+  // Undef resources typically indicate uninitialized locals being used
+  // in some code path, which we should catch and report. However, some
+  // code patterns in large shaders cause dead undef resources to momentarily,
+  // which is not an error. We must wait until cleanup passes
+  // have run to know whether we must produce an error.
+  // However, we can't leave the undef values in because they could eliminated,
+  // such as by reading from resources seen in a code path that was not taken.
+  // We avoid the problem by replacing undef values by another invalid
+  // value that we can identify later.
   for (auto &F : M.functions()) {
     if (GetHLOpcodeGroupByName(&F) == HLOpcodeGroup::HLCreateHandle) {
       Type *ResTy = F.getFunctionType()->getParamType(
         HLOperandIndex::kCreateHandleResourceOpIdx);
       UndefValue *UndefRes = UndefValue::get(ResTy);
-      for (auto U : UndefRes->users()) {
-        // Only report instruction users.
-        if (Instruction *I = dyn_cast<Instruction>(U))
-          dxilutil::EmitResMappingError(I);
+      if (!UndefRes->use_empty()) {
+        Constant *InvalidRes = ConstantAggregateZero::get(ResTy);
+        UndefRes->replaceAllUsesWith(InvalidRes);
       }
     }
   }

+ 1 - 1
lib/Transforms/IPO/PassManagerBuilder.cpp

@@ -277,7 +277,7 @@ static void addHLSLPasses(bool HLSLHighLevel, unsigned OptLevel, hlsl::HLSLExten
   MPM.add(createDxilPromoteLocalResources());
   MPM.add(createDxilPromoteStaticResources());
   // Verify no undef resource again after promotion
-  MPM.add(createFailUndefResourcePass());
+  MPM.add(createInvalidateUndefResourcesPass());
 
   MPM.add(createDxilGenerationPass(NoOpt, ExtHelper));
 

+ 1 - 1
utils/hct/hctdb.py

@@ -1560,7 +1560,7 @@ class db_dxil(object):
         add_pass('hlsl-dxil-legalize-eval-operations', 'DxilLegalizeEvalOperations', 'DXIL legalize eval operations', [])
         add_pass('dxilgen', 'DxilGenerationPass', 'HLSL DXIL Generation', [
             {'n':'NotOptimized','t':'bool','c':1}])
-        add_pass('fail-undef-resource', 'FailUndefResource', 'Fail on undef resource use', [])
+        add_pass('invalidate-undef-resource', 'InvalidateUndefResources', 'Invalidate undef resources', [])
         add_pass('simplify-inst', 'SimplifyInst', 'Simplify Instructions', [])
         add_pass('hlsl-dxil-precise', 'DxilPrecisePropagatePass', 'DXIL precise attribute propagate', [])
         add_pass('dxil-legalize-sample-offset', 'DxilLegalizeSampleOffsetPass', 'DXIL legalize sample offset', [])