浏览代码

DXC extension for DXR Payload Access Qualifiers (#3171)

This extension adds qualifiers for payload structures accompanied with semantic checks and code generation. This feature is opt-in for SM 6.6 libraries.  The information added by the developer is stored in the DXIL type system and a new metadata node is emitted during code generation. The metadata is not necessary for correct translation of DXIL, so it may be safely ignored, but it provides hints to unlock potential optimizations in payload storage between DXR shader stages.
Michael Haidl 4 年之前
父节点
当前提交
1db765ea9c
共有 38 个文件被更改,包括 3529 次插入23 次删除
  1. 22 0
      include/dxc/DXIL/DxilConstants.h
  2. 20 0
      include/dxc/DXIL/DxilMetadataHelper.h
  3. 4 0
      include/dxc/DXIL/DxilModule.h
  4. 52 0
      include/dxc/DXIL/DxilTypeSystem.h
  5. 1 0
      include/dxc/Support/HLSLOptions.h
  6. 4 0
      include/dxc/Support/HLSLOptions.td
  7. 139 1
      lib/DXIL/DxilMetadataHelper.cpp
  8. 23 0
      lib/DXIL/DxilModule.cpp
  9. 133 5
      lib/DXIL/DxilTypeSystem.cpp
  10. 10 1
      lib/DxcSupport/HLSLOptions.cpp
  11. 17 1
      tools/clang/include/clang/AST/HlslTypes.h
  12. 5 0
      tools/clang/include/clang/Basic/Attr.td
  13. 30 0
      tools/clang/include/clang/Basic/Diagnostic.h
  14. 10 0
      tools/clang/include/clang/Basic/DiagnosticGroups.td
  15. 36 0
      tools/clang/include/clang/Basic/DiagnosticSemaKinds.td
  16. 1 0
      tools/clang/include/clang/Basic/LangOptions.h
  17. 2 0
      tools/clang/include/clang/Frontend/CodeGenOptions.h
  18. 10 0
      tools/clang/include/clang/Sema/SemaHLSL.h
  19. 3 0
      tools/clang/lib/AST/ASTContextHLSL.cpp
  20. 20 0
      tools/clang/lib/AST/ASTDumper.cpp
  21. 17 0
      tools/clang/lib/AST/DeclPrinter.cpp
  22. 115 8
      tools/clang/lib/CodeGen/CGHLSLMS.cpp
  23. 68 3
      tools/clang/lib/Parse/ParseDecl.cpp
  24. 1 0
      tools/clang/lib/Sema/CMakeLists.txt
  25. 1119 0
      tools/clang/lib/Sema/SemaDXR.cpp
  26. 217 0
      tools/clang/lib/Sema/SemaHLSL.cpp
  27. 232 0
      tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/access.hlsl
  28. 508 0
      tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/combination.hlsl
  29. 21 0
      tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/extern_call.hlsl
  30. 70 0
      tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/general.hlsl
  31. 266 0
      tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/metadata.hlsl
  32. 237 0
      tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/nested_access.hlsl
  33. 28 0
      tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/structs.hlsl
  34. 41 0
      tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/trace_calls.hlsl
  35. 2 2
      tools/clang/test/HLSLFileCheck/shader_targets/library/lib_default_linkage_6_x.hlsl
  36. 2 2
      tools/clang/test/HLSLFileCheck/shader_targets/library/lib_default_linkage_internal.hlsl
  37. 3 0
      tools/clang/tools/dxcompiler/dxcompilerobj.cpp
  38. 40 0
      tools/clang/unittests/HLSL/DxilModuleTest.cpp

+ 22 - 0
include/dxc/DXIL/DxilConstants.h

@@ -1489,6 +1489,28 @@ namespace DXIL {
     CandidateProceduralPrimitive = 1,
   };
 
+  enum class PayloadAccessQualifier : uint32_t {
+    NoAccess = 0,
+    Read = 1,
+    Write = 2,
+    ReadWrite = 3
+  };
+
+  enum class PayloadAccessShaderStage : uint32_t {
+    Caller = 0,
+    Closesthit = 1,
+    Miss = 2,
+    Anyhit = 3, 
+    Invalid = 0xffffffffu
+  }; 
+
+  // Allocate 4 bits per shader stage:
+  //     bits 0-1 for payload access qualifiers
+  //     bits 2-3 reserved for future use
+  const uint32_t PayloadAccessQualifierBitsPerStage = 4;
+  const uint32_t PayloadAccessQualifierValidMaskPerStage = 3;
+  const uint32_t PayloadAccessQualifierValidMask = 0x00003333;
+
   inline bool IsValidHitGroupType(HitGroupType type) {
     return (type >= HitGroupType::Triangle && type < HitGroupType::LastEntry);
   }

+ 20 - 0
include/dxc/DXIL/DxilMetadataHelper.h

@@ -32,6 +32,7 @@ class MDNode;
 class NamedMDNode;
 class GlobalVariable;
 class StringRef;
+class Type;
 }
 
 namespace hlsl {
@@ -48,6 +49,8 @@ class DxilSampler;
 class DxilTypeSystem;
 class DxilStructAnnotation;
 class DxilFieldAnnotation;
+class DxilPayloadAnnotation;
+class DxilPayloadFieldAnnotation;
 class DxilTemplateArgAnnotation;
 class DxilFunctionAnnotation;
 class DxilParameterAnnotation;
@@ -217,6 +220,10 @@ public:
   static const unsigned kDxilFieldAnnotationPreciseTag            = 8;
   static const unsigned kDxilFieldAnnotationCBUsedTag             = 9;
 
+  // DXR Payload Annotations
+  static const unsigned kDxilPayloadAnnotationStructTag           = 0;
+  static const unsigned kDxilPayloadFieldAnnotationAccessTag      = 0;
+
   // StructAnnotation extended property tags (DXIL 1.5+ only, appended)
   static const unsigned kDxilTemplateArgumentsTag                 = 0;  // Name for name-value list of extended struct properties
   // TemplateArgument tags
@@ -249,6 +256,9 @@ public:
   static const char kDxilValidatorVersionMDName[];
   // Validator version uses the same constants for fields as kDxilVersion*
 
+  // DXR Payload Annotations metadata.
+  static const char kDxilDxrPayloadAnnotationsMDName[];
+
   // Extended shader property tags.
   static const unsigned kDxilShaderFlagsTag     = 0;
   static const unsigned kDxilGSStateTag         = 1;
@@ -414,6 +424,16 @@ public:
   llvm::Metadata *EmitDxilTemplateArgAnnotation(const DxilTemplateArgAnnotation &annotation);
   void LoadDxilTemplateArgAnnotation(const llvm::MDOperand &MDO, DxilTemplateArgAnnotation &annotation);
 
+  // DXR Payload Annotations 
+  void EmitDxrPayloadAnnotations(DxilTypeSystem &TypeSystem);
+  llvm::Metadata *EmitDxrPayloadStructAnnotation(const DxilPayloadAnnotation& SA);
+  llvm::Metadata *EmitDxrPayloadFieldAnnotation(const DxilPayloadFieldAnnotation &FA, llvm::Type* fieldType);
+  void LoadDxrPayloadAnnotationNode(const llvm::MDTuple &MDT, DxilTypeSystem &TypeSystem);
+  void LoadDxrPayloadAnnotations(DxilTypeSystem &TypeSystem);
+  void LoadDxrPayloadFieldAnnoations(const llvm::MDOperand& MDO, DxilPayloadAnnotation& SA);
+  void LoadDxrPayloadFieldAnnoation(const llvm::MDOperand &MDO, DxilPayloadFieldAnnotation &FA);
+  void LoadDxrPayloadAccessQualifiers(const llvm::MDOperand &MDO, DxilPayloadFieldAnnotation &FA);
+
   // Function props.
   llvm::MDTuple *EmitDxilFunctionProps(const hlsl::DxilFunctionProps *props,
                                        const llvm::Function *F);

+ 4 - 0
include/dxc/DXIL/DxilModule.h

@@ -166,6 +166,7 @@ public:
 
   // DXIL type system.
   DxilTypeSystem &GetTypeSystem();
+  const DxilTypeSystem &GetTypeSystem() const;
 
   /// Emit llvm.used array to make sure that optimizations do not remove unreferenced globals.
   void EmitLLVMUsed();
@@ -386,6 +387,9 @@ private:
   uint32_t m_IntermediateFlags;
   uint32_t m_AutoBindingSpace;
 
+  // porperties infered from the DXILTypeSystem
+  bool m_bHasPayloadQualifiers;
+
   std::unique_ptr<DxilSubobjects> m_pSubobjects;
 
   // m_bMetadataErrors is true if non-fatal metadata errors were encountered.

+ 52 - 0
include/dxc/DXIL/DxilTypeSystem.h

@@ -12,6 +12,7 @@
 #pragma once
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/MapVector.h"
+#include "dxc/DXIL/DxilConstants.h"
 #include "dxc/DXIL/DxilCompType.h"
 #include "dxc/DXIL/DxilInterpolationMode.h"
 
@@ -140,6 +141,46 @@ private:
 };
 
 
+/// Use this class to represent type annotation for DXR payload field.
+class DxilPayloadFieldAnnotation {
+public:
+
+  static unsigned GetBitOffsetForShaderStage(DXIL::PayloadAccessShaderStage shaderStage);
+
+  DxilPayloadFieldAnnotation() = default;
+
+  bool HasCompType() const;
+  const CompType &GetCompType() const;
+  void SetCompType(CompType::Kind kind);
+
+  uint32_t GetPayloadFieldQualifierMask() const;
+  void SetPayloadFieldQualifierMask(uint32_t fieldBitmask);
+  void AddPayloadFieldQualifier(DXIL::PayloadAccessShaderStage shaderStage, DXIL::PayloadAccessQualifier qualifier);
+  DXIL::PayloadAccessQualifier GetPayloadFieldQualifier(DXIL::PayloadAccessShaderStage shaderStage) const;
+  bool HasAnnotations() const;
+
+private:
+  CompType m_CompType;
+  unsigned m_bitmask = 0;
+};
+
+/// Use this class to represent DXR payload structures.
+class DxilPayloadAnnotation {
+  friend class DxilTypeSystem;
+
+public:
+  unsigned GetNumFields() const;
+  DxilPayloadFieldAnnotation &GetFieldAnnotation(unsigned FieldIdx);
+  const DxilPayloadFieldAnnotation &GetFieldAnnotation(unsigned FieldIdx) const;
+  const llvm::StructType *GetStructType() const;
+  void SetStructType(const llvm::StructType *Ty);
+
+private:
+  const llvm::StructType *m_pStructType;
+  std::vector<DxilPayloadFieldAnnotation> m_FieldAnnotations;
+};
+
+
 enum class DxilParamInputQual {
   In,
   Out,
@@ -192,6 +233,7 @@ private:
 class DxilTypeSystem {
 public:
   using StructAnnotationMap = llvm::MapVector<const llvm::StructType *, std::unique_ptr<DxilStructAnnotation> >;
+  using PayloadAnnotationMap = llvm::MapVector<const llvm::StructType *, std::unique_ptr<DxilPayloadAnnotation> >;
   using FunctionAnnotationMap = llvm::MapVector<const llvm::Function *, std::unique_ptr<DxilFunctionAnnotation> >;
 
   DxilTypeSystem(llvm::Module *pModule);
@@ -202,6 +244,15 @@ public:
   void EraseStructAnnotation(const llvm::StructType *pStructType);
 
   StructAnnotationMap &GetStructAnnotationMap();
+  const StructAnnotationMap &GetStructAnnotationMap() const;
+
+  DxilPayloadAnnotation *AddPayloadAnnotation(const llvm::StructType *pStructType);
+  DxilPayloadAnnotation *GetPayloadAnnotation(const llvm::StructType *pStructType);
+  const DxilPayloadAnnotation *GetPayloadAnnotation(const llvm::StructType *pStructType) const;
+  void ErasePayloadAnnotation(const llvm::StructType *pStructType);
+
+  PayloadAnnotationMap &GetPayloadAnnotationMap();
+  const PayloadAnnotationMap &GetPayloadAnnotationMap() const;
 
   DxilFunctionAnnotation *AddFunctionAnnotation(const llvm::Function *pFunction);
   DxilFunctionAnnotation *GetFunctionAnnotation(const llvm::Function *pFunction);
@@ -227,6 +278,7 @@ public:
 private:
   llvm::Module *m_pModule;
   StructAnnotationMap m_StructAnnotations;
+  PayloadAnnotationMap m_PayloadAnnotations;
   FunctionAnnotationMap m_FunctionAnnotations;
 
   DXIL::LowPrecisionMode m_LowPrecisionMode;

+ 1 - 0
include/dxc/Support/HLSLOptions.h

@@ -200,6 +200,7 @@ public:
   std::map<std::string, std::string> DxcOptimizationSelects; // OPT_opt_select
 
   bool PrintAfterAll; // OPT_print_after_all
+  bool EnablePayloadQualifiers = false; // OPT_enable_payload_qualifiers
 
   // Rewriter Options
   RewriterOpts RWOpt;

+ 4 - 0
include/dxc/Support/HLSLOptions.td

@@ -277,6 +277,10 @@ def enable_lifetime_markers : Flag<["-", "/"], "enable-lifetime-markers">, Group
   HelpText<"Enable generation of lifetime markers">;
 def disable_lifetime_markers : Flag<["-", "/"], "disable-lifetime-markers">, Group<hlslcomp_Group>, Flags<[CoreOption, HelpHidden]>,
   HelpText<"Disable generation of lifetime markers where they would be otherwise (6.6+)">;
+def enable_payload_qualifiers : Flag<["-", "/"], "enable-payload-qualifiers">, Group<hlslcomp_Group>, Flags<[CoreOption, RewriteOption, DriverOption]>,
+  HelpText<"Enables support for payload access qualifiers for raytracing payloads in SM 6.6.">;
+def disable_payload_qualifiers : Flag<["-", "/"], "disable-payload-qualifiers">, Group<hlslcomp_Group>, Flags<[CoreOption, RewriteOption, DriverOption]>,
+  HelpText<"Disables support for payload access qualifiers for raytracing payloads in SM 6.7.">;
 
 // Used with API only
 def skip_serialization : Flag<["-", "/"], "skip-serialization">, Group<hlslcore_Group>, Flags<[CoreOption, HelpHidden]>,

+ 139 - 1
lib/DXIL/DxilMetadataHelper.cpp

@@ -57,6 +57,7 @@ const char DxilMDHelper::kDxilTempAllocaMDName[]                      = "dx.temp
 const char DxilMDHelper::kDxilNonUniformAttributeMDName[]             = "dx.nonuniform";
 const char DxilMDHelper::kHLDxilResourceAttributeMDName[]             = "dx.hl.resource.attribute";
 const char DxilMDHelper::kDxilValidatorVersionMDName[]                = "dx.valver";
+const char DxilMDHelper::kDxilDxrPayloadAnnotationsMDName[]           = "dx.dxrPayloadAnnotations";
 
 // This named metadata is not valid in final module (should be moved to DxilContainer)
 const char DxilMDHelper::kDxilRootSignatureMDName[]                   = "dx.rootSignature";
@@ -77,7 +78,7 @@ const char DxilMDHelper::kDxilSourceArgsOldMDName[]                   = "llvm.db
 // This is reflection-only metadata
 const char DxilMDHelper::kDxilCountersMDName[]                        = "dx.counters";
 
-static std::array<const char *, 7> DxilMDNames = { {
+static std::array<const char *, 8> DxilMDNames = { {
   DxilMDHelper::kDxilVersionMDName,
   DxilMDHelper::kDxilShaderModelMDName,
   DxilMDHelper::kDxilEntryPointsMDName,
@@ -85,6 +86,7 @@ static std::array<const char *, 7> DxilMDNames = { {
   DxilMDHelper::kDxilTypeSystemMDName,
   DxilMDHelper::kDxilValidatorVersionMDName,
   DxilMDHelper::kDxilViewIdStateMDName,
+  DxilMDHelper::kDxilDxrPayloadAnnotationsMDName,
 }};
 
 DxilMDHelper::DxilMDHelper(Module *pModule, std::unique_ptr<ExtraPropertyHelper> EPH)
@@ -850,6 +852,130 @@ void DxilMDHelper::LoadDxilTypeSystem(DxilTypeSystem &TypeSystem) {
   }
 }
 
+void DxilMDHelper::EmitDxrPayloadAnnotations(DxilTypeSystem &TypeSystem) {
+  auto &TypeMap = TypeSystem.GetPayloadAnnotationMap();
+  vector<Metadata *> MDVals;
+  MDVals.emplace_back(Uint32ToConstMD(kDxilPayloadAnnotationStructTag)); // Tag
+  unsigned GVIdx = 0;
+  for (auto it = TypeMap.begin(); it != TypeMap.end(); ++it, GVIdx++) {
+    StructType *pStructType = const_cast<StructType *>(it->first);
+    DxilPayloadAnnotation *pA = it->second.get();
+    // Emit struct type field annotations.
+    Metadata *pMD = EmitDxrPayloadStructAnnotation(*pA);
+
+    MDVals.push_back(ValueAsMetadata::get(UndefValue::get(pStructType)));
+    MDVals.push_back(pMD);
+  }
+
+  NamedMDNode *pDxrPayloadAnnotationsMD = m_pModule->getNamedMetadata(kDxilDxrPayloadAnnotationsMDName);
+  if (pDxrPayloadAnnotationsMD != nullptr) {
+    m_pModule->eraseNamedMetadata(pDxrPayloadAnnotationsMD);
+  }
+
+  if (MDVals.size() > 1) {
+    pDxrPayloadAnnotationsMD = m_pModule->getOrInsertNamedMetadata(kDxilDxrPayloadAnnotationsMDName);
+    pDxrPayloadAnnotationsMD->addOperand(MDNode::get(m_Ctx, MDVals));
+  }
+}
+
+Metadata *
+DxilMDHelper::EmitDxrPayloadStructAnnotation(const DxilPayloadAnnotation &SA) {
+  vector<Metadata *> MDVals;
+  MDVals.reserve(SA.GetNumFields());
+  MDVals.resize(SA.GetNumFields());
+
+  const StructType* STy = SA.GetStructType();
+  for (unsigned i = 0; i < SA.GetNumFields(); i++) {
+    MDVals[i] = EmitDxrPayloadFieldAnnotation(SA.GetFieldAnnotation(i), STy->getElementType(i));
+  }
+
+  return MDNode::get(m_Ctx, MDVals);
+}
+
+void DxilMDHelper::LoadDxrPayloadAccessQualifiers(const MDOperand &MDO,
+                                               DxilPayloadFieldAnnotation &FA) {
+  unsigned fieldBitmask = ConstMDToInt32(MDO);
+  if (fieldBitmask & ~DXIL::PayloadAccessQualifierValidMask) {
+    DXASSERT(false, "Unknown payload access qualifier bits set");
+    m_bExtraMetadata = true;
+  }
+  fieldBitmask &= DXIL::PayloadAccessQualifierValidMask;
+  FA.SetPayloadFieldQualifierMask(fieldBitmask);
+}
+
+void DxilMDHelper::LoadDxrPayloadFieldAnnoation(
+    const MDOperand &MDO, DxilPayloadFieldAnnotation &FA) {
+  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get()); // Tag-Value list.
+  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0, DXC_E_INCORRECT_DXIL_METADATA);
+
+  for (unsigned i = 0; i < pTupleMD->getNumOperands(); i += 2) {
+    unsigned Tag = ConstMDToUint32(pTupleMD->getOperand(i));
+    const MDOperand &MDO = pTupleMD->getOperand(i + 1);
+    IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+
+    switch (Tag) {
+    case kDxilPayloadFieldAnnotationAccessTag:
+      LoadDxrPayloadAccessQualifiers(MDO, FA);
+      break;
+    default:
+      DXASSERT(false, "Unknown payload field annotation tag");
+      m_bExtraMetadata = true;
+      break;
+    }
+  }
+}
+
+void DxilMDHelper::LoadDxrPayloadFieldAnnoations(const MDOperand &MDO,
+                                                DxilPayloadAnnotation &SA) {
+  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
+  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+  IFTBOOL(pTupleMD->getNumOperands() == SA.GetNumFields(),
+          DXC_E_INCORRECT_DXIL_METADATA);
+  for (unsigned i = 0; i < SA.GetNumFields(); ++i) {
+    LoadDxrPayloadFieldAnnoation(pTupleMD->getOperand(i), SA.GetFieldAnnotation(i));
+  }
+}
+
+void DxilMDHelper::LoadDxrPayloadAnnotationNode(const llvm::MDTuple &MDT,
+                                                DxilTypeSystem &TypeSystem) {
+  unsigned Tag = ConstMDToUint32(MDT.getOperand(0));
+  IFTBOOL(Tag == kDxilPayloadAnnotationStructTag, DXC_E_INCORRECT_DXIL_METADATA)
+  IFTBOOL((MDT.getNumOperands() & 0x1) == 1, DXC_E_INCORRECT_DXIL_METADATA);
+
+  Constant *pGV = dyn_cast<Constant>(ValueMDToValue(MDT.getOperand(1)));
+  IFTBOOL(pGV != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+  StructType *pGVType = dyn_cast<StructType>(pGV->getType());
+  IFTBOOL(pGVType != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+
+  // Check if this struct is already part of the DXIL Type System
+  DxilPayloadAnnotation *pPA = TypeSystem.AddPayloadAnnotation(pGVType);
+
+  LoadDxrPayloadFieldAnnoations(MDT.getOperand(2), *pPA);
+}
+
+void DxilMDHelper::LoadDxrPayloadAnnotations(DxilTypeSystem &TypeSystem) {
+  NamedMDNode *pDxilPayloadAnnotationsMD =
+      m_pModule->getNamedMetadata(kDxilDxrPayloadAnnotationsMDName);
+  if (pDxilPayloadAnnotationsMD == nullptr)
+    return;
+
+  if (DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 6) < 0) {
+    DXASSERT(false, "payload access qualifier emitted for dxil version < 1.6");
+    m_bExtraMetadata = true;
+  }
+  DXASSERT(pDxilPayloadAnnotationsMD->getNumOperands() != 0, "empty metadata node?");
+
+  for (unsigned i = 0; i < pDxilPayloadAnnotationsMD->getNumOperands(); i++) {
+    const MDTuple *pTupleMD =
+        dyn_cast<MDTuple>(pDxilPayloadAnnotationsMD->getOperand(i));
+    IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+    LoadDxrPayloadAnnotationNode(*pTupleMD, TypeSystem);
+  }
+}
+
 Metadata *DxilMDHelper::EmitDxilTemplateArgAnnotation(const DxilTemplateArgAnnotation &annotation) {
   SmallVector<Metadata *, 2> MDVals;
   if (annotation.IsType()) {
@@ -1065,6 +1191,7 @@ Metadata *DxilMDHelper::EmitDxilFieldAnnotation(const DxilFieldAnnotation &FA) {
   return MDNode::get(m_Ctx, MDVals);
 }
 
+
 void DxilMDHelper::LoadDxilFieldAnnotation(const MDOperand &MDO, DxilFieldAnnotation &FA) {
   IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
   const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
@@ -1116,6 +1243,17 @@ void DxilMDHelper::LoadDxilFieldAnnotation(const MDOperand &MDO, DxilFieldAnnota
   }
 }
 
+Metadata *
+DxilMDHelper::EmitDxrPayloadFieldAnnotation(const DxilPayloadFieldAnnotation &FA, Type* fieldType) {
+  vector<Metadata *> MDVals; // Tag-Value list.
+  MDVals.emplace_back(Uint32ToConstMD(kDxilPayloadFieldAnnotationAccessTag));
+
+  auto mask = FA.GetPayloadFieldQualifierMask();
+  MDVals.emplace_back(Uint32ToConstMD(mask));
+
+  return MDNode::get(m_Ctx, MDVals);
+}
+
 const Function *DxilMDHelper::LoadDxilFunctionProps(const MDTuple *pProps,
                                               hlsl::DxilFunctionProps *props) {
   unsigned idx = 0;

+ 23 - 0
lib/DXIL/DxilModule.cpp

@@ -1318,6 +1318,10 @@ DxilTypeSystem &DxilModule::GetTypeSystem() {
   return *m_pTypeSystem;
 }
 
+const DxilTypeSystem &DxilModule::GetTypeSystem() const {
+  return *m_pTypeSystem;
+}
+
 std::vector<unsigned> &DxilModule::GetSerializedViewIdState() {
   return m_SerializedState;
 }
@@ -1453,6 +1457,14 @@ void DxilModule::EmitDxilMetadata() {
        (m_ValMajor > 1 || (m_ValMajor == 1 && m_ValMinor >= 1)))) {
     m_pMDHelper->EmitDxilViewIdState(m_SerializedState);
   }
+
+  // Emit the DXR Payload Annotations only for library Dxil 1.6 and above.
+  if (m_pSM->IsLib()) {
+    if (DXIL::CompareVersions(m_DxilMajor, m_DxilMinor, 1, 6) >= 0) {
+      m_pMDHelper->EmitDxrPayloadAnnotations(GetTypeSystem());
+    }
+  }
+
   EmitLLVMUsed();
   MDTuple *pEntry = m_pMDHelper->EmitDxilEntryPointTuple(GetEntryFunction(), m_EntryName, pMDSignatures, pMDResources, pMDProperties);
   vector<MDNode *> Entries;
@@ -1607,6 +1619,17 @@ void DxilModule::LoadDxilMetadata() {
     m_pTypeSystem->GetFunctionAnnotationMap().clear();
   }
 
+  // Payload annotations not required for consumption of dxil.
+  try {
+    m_pMDHelper->LoadDxrPayloadAnnotations(*m_pTypeSystem.get());
+  } catch (hlsl::Exception &) {
+    m_bMetadataErrors = true;
+#ifdef DBG
+    throw;
+#endif
+    m_pTypeSystem->GetPayloadAnnotationMap().clear();
+  }
+
   m_pMDHelper->LoadRootSignature(m_SerializedRootSignature);
 
   m_pMDHelper->LoadDxilViewIdState(m_SerializedState);

+ 133 - 5
lib/DXIL/DxilTypeSystem.cpp

@@ -80,7 +80,69 @@ void DxilFieldAnnotation::SetFieldName(const std::string &FieldName) { m_FieldNa
 bool DxilFieldAnnotation::IsCBVarUsed() const { return m_bCBufferVarUsed; }
 void DxilFieldAnnotation::SetCBVarUsed(bool used) { m_bCBufferVarUsed = used; }
 
+//------------------------------------------------------------------------------
+//
+// DxilPayloadFieldAnnotation class methods.
+//
+bool DxilPayloadFieldAnnotation::HasCompType() const { return m_CompType.GetKind() != CompType::Kind::Invalid; }
+const CompType &DxilPayloadFieldAnnotation::GetCompType() const { return m_CompType; }
+void DxilPayloadFieldAnnotation::SetCompType(CompType::Kind kind) { m_CompType = CompType(kind); }
+uint32_t DxilPayloadFieldAnnotation::GetPayloadFieldQualifierMask() const {
+  return m_bitmask;
+}
+
+unsigned DxilPayloadFieldAnnotation::GetBitOffsetForShaderStage(DXIL::PayloadAccessShaderStage shaderStage ) {
+  unsigned bitOffset = static_cast<unsigned>(shaderStage) *
+                       DXIL::PayloadAccessQualifierBitsPerStage;
+  return bitOffset;
+}
+
+void DxilPayloadFieldAnnotation::SetPayloadFieldQualifierMask(uint32_t fieldBitmask) {
+  DXASSERT((fieldBitmask & ~DXIL::PayloadAccessQualifierValidMask) == 0,
+           "Unknown payload access qualifier bits set");
+  m_bitmask = fieldBitmask & DXIL::PayloadAccessQualifierValidMask;
+}
+
+void DxilPayloadFieldAnnotation::AddPayloadFieldQualifier(
+    DXIL::PayloadAccessShaderStage shaderStage, DXIL::PayloadAccessQualifier qualifier) {
+  unsigned accessBits = static_cast<unsigned>(qualifier);
+  DXASSERT((accessBits & ~DXIL::PayloadAccessQualifierValidMaskPerStage) == 0,
+           "Unknown payload access qualifier bits set");
+  accessBits &= DXIL::PayloadAccessQualifierValidMaskPerStage;
 
+  accessBits <<= GetBitOffsetForShaderStage(shaderStage);
+  m_bitmask |= accessBits;
+}
+
+DXIL::PayloadAccessQualifier DxilPayloadFieldAnnotation::GetPayloadFieldQualifier(
+    DXIL::PayloadAccessShaderStage shaderStage) const {
+
+  int bitOffset = GetBitOffsetForShaderStage(shaderStage);
+
+  // default type is always ReadWrite
+  DXIL::PayloadAccessQualifier accessType = DXIL::PayloadAccessQualifier::ReadWrite;
+
+  const unsigned readBit = static_cast<unsigned>(DXIL::PayloadAccessQualifier::Read);
+  const unsigned writeBit = static_cast<unsigned>(DXIL::PayloadAccessQualifier::Write);
+
+  unsigned accessBits = m_bitmask >> bitOffset;
+  if (accessBits & readBit) {
+    // set Read if the first bit is set
+    accessType = DXIL::PayloadAccessQualifier::Read;
+  }
+  if (accessBits & writeBit) {
+
+    // set Write only if the second bit set, if both are set set to ReadWrite
+    accessType = accessType == DXIL::PayloadAccessQualifier::ReadWrite
+                     ? DXIL::PayloadAccessQualifier::Write
+                     : DXIL::PayloadAccessQualifier::ReadWrite;
+  }
+  return accessType;
+}
+
+bool DxilPayloadFieldAnnotation::HasAnnotations() const {
+  return m_bitmask != 0;
+}
 
 //------------------------------------------------------------------------------
 //
@@ -98,10 +160,6 @@ bool DxilTemplateArgAnnotation::IsIntegral() const { return m_Type == nullptr; }
 int64_t DxilTemplateArgAnnotation::GetIntegral() const { return m_Integral; }
 void DxilTemplateArgAnnotation::SetIntegral(int64_t i64) { m_Type = nullptr; m_Integral = i64; }
 
-//------------------------------------------------------------------------------
-//
-// DxilStructAnnotation class methods.
-//
 unsigned DxilStructAnnotation::GetNumFields() const {
   return (unsigned)m_FieldAnnotations.size();
 }
@@ -200,7 +258,30 @@ const Function *DxilFunctionAnnotation::GetFunction() const {
 
 //------------------------------------------------------------------------------
 //
-// DxilStructAnnotationSystem class methods.
+// DxilPayloadAnnotation class methods.
+//
+unsigned DxilPayloadAnnotation::GetNumFields() const {
+  return (unsigned)m_FieldAnnotations.size();
+}
+
+DxilPayloadFieldAnnotation &DxilPayloadAnnotation::GetFieldAnnotation(unsigned FieldIdx) {
+  return m_FieldAnnotations[FieldIdx];
+}
+
+const DxilPayloadFieldAnnotation &DxilPayloadAnnotation::GetFieldAnnotation(unsigned FieldIdx) const {
+  return m_FieldAnnotations[FieldIdx];
+}
+
+const StructType *DxilPayloadAnnotation::GetStructType() const {
+  return m_pStructType;
+}
+void DxilPayloadAnnotation::SetStructType(const llvm::StructType *Ty) {
+  m_pStructType = Ty;
+}
+
+//------------------------------------------------------------------------------
+//
+// DxilTypeSystem class methods.
 //
 DxilTypeSystem::DxilTypeSystem(Module *pModule)
     : m_pModule(pModule),
@@ -246,6 +327,53 @@ DxilTypeSystem::StructAnnotationMap &DxilTypeSystem::GetStructAnnotationMap() {
   return m_StructAnnotations;
 }
 
+const DxilTypeSystem::StructAnnotationMap &DxilTypeSystem::GetStructAnnotationMap() const{
+  return m_StructAnnotations;
+}
+
+DxilPayloadAnnotation *DxilTypeSystem::AddPayloadAnnotation(const StructType *pStructType) {
+  DXASSERT_NOMSG(m_PayloadAnnotations.find(pStructType) == m_PayloadAnnotations.end());
+  DxilPayloadAnnotation *pA = new DxilPayloadAnnotation();
+  m_PayloadAnnotations[pStructType] = unique_ptr<DxilPayloadAnnotation>(pA);
+  pA->m_pStructType = pStructType;
+  pA->m_FieldAnnotations.resize(pStructType->getNumElements());
+  return pA;
+}
+
+DxilPayloadAnnotation *DxilTypeSystem::GetPayloadAnnotation(const StructType *pStructType) {
+  auto it = m_PayloadAnnotations.find(pStructType);
+  if (it != m_PayloadAnnotations.end()) {
+    return it->second.get();
+  } else {
+    return nullptr;
+  }
+}
+
+const DxilPayloadAnnotation *
+DxilTypeSystem::GetPayloadAnnotation(const StructType *pStructType) const {
+  auto it = m_PayloadAnnotations.find(pStructType);
+  if (it != m_PayloadAnnotations.end()) {
+    return it->second.get();
+  } else {
+    return nullptr;
+  }
+}
+
+void DxilTypeSystem::ErasePayloadAnnotation(const StructType *pStructType) {
+  DXASSERT_NOMSG(m_StructAnnotations.count(pStructType));
+  m_PayloadAnnotations.remove_if([pStructType](
+      const std::pair<const StructType *, std::unique_ptr<DxilPayloadAnnotation>>
+          &I) { return pStructType == I.first; });
+}
+
+DxilTypeSystem::PayloadAnnotationMap &DxilTypeSystem::GetPayloadAnnotationMap() {
+  return m_PayloadAnnotations;
+}
+
+const DxilTypeSystem::PayloadAnnotationMap &DxilTypeSystem::GetPayloadAnnotationMap() const{
+  return m_PayloadAnnotations;
+}
+
 DxilFunctionAnnotation *DxilTypeSystem::AddFunctionAnnotation(const Function *pFunction) {
   DXASSERT_NOMSG(m_FunctionAnnotations.find(pFunction) == m_FunctionAnnotations.end());
   DxilFunctionAnnotation *pA = new DxilFunctionAnnotation();

+ 10 - 1
lib/DxcSupport/HLSLOptions.cpp

@@ -652,7 +652,16 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
   // Lifetime markers on by default in 6.6 unless disabled explicitly
   opts.EnableLifetimeMarkers = Args.hasFlag(OPT_enable_lifetime_markers, OPT_INVALID,
                                             DXIL::CompareVersions(Major, Minor, 6, 6) >= 0) &&
-                               !Args.hasFlag(OPT_disable_lifetime_markers, OPT_INVALID, false);
+                              !Args.hasFlag(OPT_disable_lifetime_markers, OPT_INVALID, false);
+  opts.EnablePayloadQualifiers = Args.hasFlag(OPT_enable_payload_qualifiers, OPT_INVALID,
+                                            DXIL::CompareVersions(Major, Minor, 6, 7) >= 0); 
+  if (DXIL::CompareVersions(Major, Minor, 6, 8) < 0) {
+     opts.EnablePayloadQualifiers &= !Args.hasFlag(OPT_disable_payload_qualifiers, OPT_INVALID, false);
+  }
+  if (opts.EnablePayloadQualifiers && DXIL::CompareVersions(Major, Minor, 6, 6) < 0) {
+    errors << "Invalid target for payload access qualifiers. Only lib_6_6 and beyond are supported.";
+    return 1;
+  }
 
   if (opts.DefaultColMajor && opts.DefaultRowMajor) {
     errors << "Cannot specify /Zpr and /Zpc together, use /? to get usage information";

+ 17 - 1
tools/clang/include/clang/AST/HlslTypes.h

@@ -199,7 +199,8 @@ public:
   enum UnusualAnnotationKind {
     UA_RegisterAssignment,
     UA_ConstantPacking,
-    UA_SemanticDecl
+    UA_SemanticDecl,
+    UA_PayloadAccessQualifier
   };
 private:
   const UnusualAnnotationKind Kind;
@@ -243,6 +244,21 @@ struct RegisterAssignment : public UnusualAnnotation
   }
 };
 
+// <summary>Use this structure to capture a ': in/out' definiton.</summary>
+struct PayloadAccessAnnotation: public UnusualAnnotation {
+  /// <summary>Initializes a new PayloadAccessAnnotation in invalid state.</summary>
+  PayloadAccessAnnotation() : UnusualAnnotation(UA_PayloadAccessQualifier){};
+
+  DXIL::PayloadAccessQualifier qualifier = DXIL::PayloadAccessQualifier::NoAccess;
+  
+  llvm::SmallVector<DXIL::PayloadAccessShaderStage, 4> ShaderStages;
+
+  static bool classof(const UnusualAnnotation *UA) {
+    return UA->getKind() == UA_PayloadAccessQualifier;
+  }
+};
+
+
 /// <summary>Use this structure to capture a ': packoffset' definition.</summary>
 struct ConstantPacking : public UnusualAnnotation
 {

+ 5 - 0
tools/clang/include/clang/Basic/Attr.td

@@ -895,6 +895,11 @@ def HLSLPayload : InheritableAttr {
   let Documentation = [Undocumented];
 }
 
+def HLSLRayPayload : InheritableAttr {
+  let Spellings = [CXX11<"", "raypayload", 2015>];
+  let Documentation = [Undocumented];
+}
+
 def HLSLWaveSensitive : InheritableAttr {
   let Spellings = [CXX11<"", "wavesensitive", 2015>];
   let Subjects = SubjectList<[ParmVar]>;

+ 30 - 0
tools/clang/include/clang/Basic/Diagnostic.h

@@ -23,6 +23,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/iterator_range.h"
+#include "llvm/ADT/SmallVector.h"
 #include <list>
 #include <vector>
 
@@ -674,6 +675,17 @@ public:
 
   void Report(const StoredDiagnostic &storedDiag);
 
+  /// \brief Issue the message to the client but only once.
+  ///
+  /// This actually returns an instance of DiagnosticBuilder which emits the
+  /// diagnostics (through @c ProcessDiag) when it is destroyed.
+  ///
+  /// \param DiagID A member of the @c diag::kind enum.
+  /// \param Loc Represents the source location associated with the diagnostic,
+  /// which can be an invalid location if no position information is available.
+  inline DiagnosticBuilder ReportOnce(unsigned DiagID);
+  inline DiagnosticBuilder ReportOnce(SourceLocation Loc, unsigned DiagID);
+
   /// \brief Determine whethere there is already a diagnostic in flight.
   bool isDiagnosticInFlight() const { return CurDiagID != ~0U; }
 
@@ -726,6 +738,9 @@ private:
   /// \brief The location of the current diagnostic that is in flight.
   SourceLocation CurDiagLoc;
 
+  /// \brief Stores Diagnostics that should be onyl remited once.
+  llvm::SmallVector<unsigned, 2> DiagOnceDiagnostics;
+
   /// \brief The ID of the current diagnostic that is in flight.
   ///
   /// This is set to ~0U when there is no diagnostic in flight.
@@ -1126,10 +1141,25 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(SourceLocation Loc,
   return DiagnosticBuilder(this);
 }
 
+
 inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) {
   return Report(SourceLocation(), DiagID);
 }
 
+
+inline DiagnosticBuilder DiagnosticsEngine::ReportOnce(unsigned DiagID) {
+  return ReportOnce(SourceLocation(), DiagID);
+}
+
+inline DiagnosticBuilder DiagnosticsEngine::ReportOnce(SourceLocation Loc,
+                                                       unsigned DiagID) {
+  if (std::find(DiagOnceDiagnostics.begin(), DiagOnceDiagnostics.end(),
+                DiagID) != DiagOnceDiagnostics.end())
+    return DiagnosticBuilder(this);
+
+  DiagOnceDiagnostics.push_back(DiagID);
+  return Report(Loc, DiagID);
+}
 //===----------------------------------------------------------------------===//
 // Diagnostic
 //                                                                           //

+ 10 - 0
tools/clang/include/clang/Basic/DiagnosticGroups.td

@@ -787,4 +787,14 @@ def HLSLSpecifierOverride : DiagGroup<"specifier-override">;
 def HLSLPackOffsetOverride : DiagGroup<"packoffset-override">;
 def HLSLCommaInInit : DiagGroup<"comma-in-init">;
 def HLSLAmbigLitShift : DiagGroup<"ambig-lit-shift">;
+def HLSLPayloadAccessQualiferTrace: DiagGroup<"payload-access-trace">;
+def HLSLPayloadAccessQualiferShader: DiagGroup<"payload-access-shader">;
+def HLSLPayloadAccessQualiferPerf: DiagGroup<"payload-access-perf">;
+def HLSLPayloadAccessQualiferCall: DiagGroup<"payload-access-call">;
+def HLSLPayloadAccessQualifer: DiagGroup<"payload-access-qualifier", [
+     HLSLPayloadAccessQualiferTrace,
+     HLSLPayloadAccessQualiferShader,
+     HLSLPayloadAccessQualiferPerf,
+     HLSLPayloadAccessQualiferCall
+  ]>;
 // HLSL Change Ends

+ 36 - 0
tools/clang/include/clang/Basic/DiagnosticSemaKinds.td

@@ -7540,6 +7540,42 @@ def err_hlsl_unsupported_buffer_packoffset : Error<
   "packoffset is only allowed within a constant buffer, not on the constant buffer declaration">;
 def err_hlsl_unsupported_buffer_slot_target_specific : Error<
   "user defined constant buffer slots cannot be target specific">;
+def err_hlsl_unsupported_payload_access_qualifier : Error<
+  "payload access qualifiers are only allowed for member variables of a payload structure">;
+def err_hlsl_unsupported_payload_access_qualifier_struct : Error<
+  "payload access qualifiers are not supported on struct types.">;
+def err_hlsl_payload_access_qualifier_unsupported_shader : Error<
+  "payload access qualifiers are only defined for raytracing shader stages closesthit, miss, anyhit and for special keyword: caller. '%0' is not supported">;
+def err_hlsl_payload_access_qualifier_invalid_combination : Error<
+  "field %0 is qualified '%1' for shader stage '%2' but has no valid %3">;
+def err_hlsl_payload_access_qualifier_multiple_defined : Error<
+  "payload access qualifier '%0' has already been defined">;
+def warn_hlsl_payload_access_data_loss : Warning<
+  "potential loss of data for payload field '%0'. Field is qualified 'write' in earlier stages and 'write' only for stage '%1' but never unconditionally written.">, InGroup<HLSLPayloadAccessQualiferShader>;
+def warn_hlsl_payload_access_undef_read : Warning<
+  "reading undefined value ('%0' is not qualified 'read' for shader stage '%1')">, InGroup<HLSLPayloadAccessQualiferShader>;
+def warn_hlsl_payload_access_write_loss : Warning<
+  "write will be dropped ('%0' is not qualified 'write' for shader stage '%1')">, InGroup<HLSLPayloadAccessQualiferShader>;
+def warn_hlsl_payload_access_no_write_for_trace_payload : Warning<
+  "field '%0' is 'write' for 'caller' stage but field is never written for TraceRay call">, InGroup<HLSLPayloadAccessQualiferTrace>;
+def warn_hlsl_payload_access_write_but_no_write_for_trace_payload : Warning<
+  "value will be undefined inside TraceRay ('%0' is not qualified 'write' for 'caller')">, InGroup<HLSLPayloadAccessQualiferTrace>;
+def warn_hlsl_payload_access_read_of_undef_after_trace : Warning<
+  "reading undefined value ('%0' is returned from TraceRay but not qualified 'read' for 'caller')">, InGroup<HLSLPayloadAccessQualiferTrace>;
+def warn_hlsl_payload_access_read_but_no_read_after_trace : Warning<
+  "'%0' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)">, InGroup<HLSLPayloadAccessQualiferPerf>;
+def warn_qualified_payload_passed_to_extern_function : Warning<
+  "passing a qualified payload to an extern function can cause undefined behavior if payload qualifiers mismatch">, InGroup<HLSLPayloadAccessQualiferCall>;
+def err_not_all_payload_fields_qualified : Error<
+  "payload type '%0' requires that all fields carry payload access qualifiers.">;
+def err_payload_requires_attribute : Error<
+  "type '%0' used as payload requires that it is annotated with the [raypayload] attribute">;
+def err_payload_fields_not_qualified : Error<
+  "payload field '%0' has no payload access qualifiers.">;
+def err_payload_fields_is_payload_and_overqualified : Error<
+  "payload field '%0' is a payload struct. Payload access qualifiers are not allowed on payload types.">;
+def warn_hlsl_payload_qualifer_dropped : Warning<
+  "payload access qualifieres are only supported for target lib_6_6 and beyond. You can opt-in for lib_6_6 with the -enable-payload-qualifiers flag. Qualifiers will be dropped.">, InGroup<HLSLPayloadAccessQualifer>;
 def err_hlsl_unsupported_builtin_op: Error<
   "operator cannot be used with built-in type %0">;
 def err_hlsl_unsupported_char_literal : Error<

+ 1 - 0
tools/clang/include/clang/Basic/LangOptions.h

@@ -158,6 +158,7 @@ public:
   bool UseMinPrecision; // use min precision, not native precision.
   bool EnableDX9CompatMode;
   bool EnableFXCCompatMode;
+  bool EnablePayloadAccessQualifiers;
   // HLSL Change Ends
 
   bool SPIRV = false;  // SPIRV Change

+ 2 - 0
tools/clang/include/clang/Frontend/CodeGenOptions.h

@@ -234,6 +234,8 @@ public:
   bool HLSLEnableLifetimeMarkers = false;
   /// Put shader sources and options in the module
   bool HLSLEmbedSourcesInModule = false;
+  /// Enable generation of payload access qualifier metadata. 
+  bool HLSLEnablePayloadAccessQualifiers = false;
   // HLSL Change Ends
 
   // SPIRV Change Starts

+ 10 - 0
tools/clang/include/clang/Sema/SemaHLSL.h

@@ -86,6 +86,16 @@ void DiagnoseUnusualAnnotationsForHLSL(
   clang::Sema& S,
   std::vector<hlsl::UnusualAnnotation *>& annotations);
 
+void DiagnosePayloadAccessQualifierAnnotations(
+  clang::Sema &S,
+  clang::Declarator& D,
+  const clang::QualType& T,
+  const std::vector<hlsl::UnusualAnnotation *> &annotations);
+
+void DiagnoseRaytracingPayloadAccess(
+  clang::Sema &S,
+  clang::TranslationUnitDecl* TU);
+
 /// <summary>Finds the best viable function on this overload set, if it exists.</summary>
 clang::OverloadingResult GetBestViableFunction(
   clang::Sema &S,

+ 3 - 0
tools/clang/lib/AST/ASTContextHLSL.cpp

@@ -1169,6 +1169,9 @@ UnusualAnnotation* hlsl::UnusualAnnotation::CopyToASTContext(ASTContext& Context
     break;
   case UA_ConstantPacking:
     instanceSize = sizeof(hlsl::ConstantPacking);
+    break;  
+  case UA_PayloadAccessQualifier:
+    instanceSize = sizeof(hlsl::PayloadAccessAnnotation);
     break;
   default:
     DXASSERT(Kind == UA_SemanticDecl, "Kind == UA_SemanticDecl -- otherwise switch is incomplete");

+ 20 - 0
tools/clang/lib/AST/ASTDumper.cpp

@@ -994,6 +994,8 @@ void ASTDumper::dumpHLSLUnusualAnnotations(const ArrayRef<hlsl::UnusualAnnotatio
             OS << "RegisterAssignment"; break;
           case hlsl::UnusualAnnotation::UA_SemanticDecl:
             OS << "SemanticDecl"; break;
+          case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier:
+            OS << "PayloadAccessQualifier"; break;
         }
       }
       dumpPointer(It);
@@ -1043,7 +1045,25 @@ void ASTDumper::dumpHLSLUnusualAnnotations(const ArrayRef<hlsl::UnusualAnnotatio
           const hlsl::SemanticDecl* semanticDecl = cast<hlsl::SemanticDecl>(*It);
           OS << " \"" << semanticDecl->SemanticName << "\"";
           break;
+        }      
+      case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier: {
+        const hlsl::PayloadAccessAnnotation *annotation =
+            cast<hlsl::PayloadAccessAnnotation>(*It);
+        OS << " "
+           << (annotation->qualifier == hlsl::DXIL::PayloadAccessQualifier::Read
+                   ? "read"
+                   : "write")
+           << "(";
+        StringRef shaderStageNames[] = {"caller", "closesthit", "miss", "anyhit"};
+        for (unsigned i = 0; i < annotation->ShaderStages.size(); ++i) {
+          OS << shaderStageNames[static_cast<unsigned>(
+              annotation->ShaderStages[i])];
+          if (i < annotation->ShaderStages.size() - 1)
+            OS << ", ";
         }
+        OS << ")";
+        break;
+      }
       }
     });
   }

+ 17 - 0
tools/clang/lib/AST/DeclPrinter.cpp

@@ -1495,6 +1495,23 @@ void DeclPrinter::VisitHLSLUnusualAnnotation(const hlsl::UnusualAnnotation *UA)
     Out << ")";
     break;
   }
+  case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier: {
+    const hlsl::PayloadAccessAnnotation *annotation =
+        cast<hlsl::PayloadAccessAnnotation>(UA);
+    Out << " : "
+        << (annotation->qualifier == hlsl::DXIL::PayloadAccessQualifier::Read
+                ? "read"
+                : "write")
+        << "(";
+    StringRef shaderStageNames[] = { "caller", "closesthit", "miss", "anyhit"};
+    for (unsigned i = 0; i < annotation->ShaderStages.size(); ++i) {
+      Out << shaderStageNames[static_cast<unsigned>(annotation->ShaderStages[i])];
+      if (i < annotation->ShaderStages.size() - 1)
+        Out << ", ";
+    }
+    Out << ")";
+    break;
+  }
   }
 }
 

+ 115 - 8
tools/clang/lib/CodeGen/CGHLSLMS.cpp

@@ -204,6 +204,7 @@ private:
 
   // Type annotation related.
   unsigned ConstructStructAnnotation(DxilStructAnnotation *annotation,
+                                     DxilPayloadAnnotation* payloadAnnotation,
                                      const RecordDecl *RD,
                                      DxilTypeSystem &dxilTypeSys);
   unsigned AddTypeAnnotation(QualType Ty, DxilTypeSystem &dxilTypeSys,
@@ -920,6 +921,7 @@ static unsigned AlignBaseOffset(QualType Ty, unsigned baseOffset,
 }
 
 unsigned CGMSHLSLRuntime::ConstructStructAnnotation(DxilStructAnnotation *annotation,
+                                      DxilPayloadAnnotation* payloadAnnotation,
                                       const RecordDecl *RD,
                                       DxilTypeSystem &dxilTypeSys) {
   unsigned fieldIdx = 0;
@@ -992,6 +994,9 @@ unsigned CGMSHLSLRuntime::ConstructStructAnnotation(DxilStructAnnotation *annota
 
     unsigned CBufferOffset = offset;
 
+    DxilFieldAnnotation &fieldAnnotation = annotation->GetFieldAnnotation(fieldIdx++);
+    ConstructFieldAttributedAnnotation(fieldAnnotation, fieldTy, bDefaultRowMajor);
+
     // Try to get info from fieldDecl.
     for (const hlsl::UnusualAnnotation *it :
          fieldDecl->getUnusualAnnotations()) {
@@ -1016,6 +1021,21 @@ unsigned CGMSHLSLRuntime::ConstructStructAnnotation(DxilStructAnnotation *annota
         Diags.Report(it->Loc, DiagID);
         return 0;
       } break;
+      case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier: {
+        // Forward payload access qualifiers to fieldAnnotation. 
+        if (payloadAnnotation) {
+          const hlsl::PayloadAccessAnnotation *annotation =
+              cast<hlsl::PayloadAccessAnnotation>(it);
+          DxilPayloadFieldAnnotation &payloadFieldAnnotation =
+              payloadAnnotation->GetFieldAnnotation(fieldIdx - 1);
+          payloadFieldAnnotation.SetCompType(
+              fieldAnnotation.GetCompType().GetKind());
+          for (auto stage : annotation->ShaderStages) {
+            payloadFieldAnnotation.AddPayloadFieldQualifier(
+                stage, annotation->qualifier);
+          }
+        }
+      } break;
       default:
         llvm_unreachable("only semantic for input/output");
         break;
@@ -1029,9 +1049,6 @@ unsigned CGMSHLSLRuntime::ConstructStructAnnotation(DxilStructAnnotation *annota
     // Update offset.
     offset += size;
     
-    DxilFieldAnnotation &fieldAnnotation = annotation->GetFieldAnnotation(fieldIdx++);
-
-    ConstructFieldAttributedAnnotation(fieldAnnotation, fieldTy, bDefaultRowMajor);
     ConstructFieldInterpolation(fieldAnnotation, fieldDecl);
     if (fieldDecl->hasAttr<HLSLPreciseAttr>())
       fieldAnnotation.SetPrecise();
@@ -1068,6 +1085,65 @@ static unsigned GetNumTemplateArgsForRecordDecl(const RecordDecl *RD) {
   return 0;
 }
 
+static bool ValidatePayloadDecl(const RecordDecl *Decl,
+                                const ShaderModel &Model,
+                                DiagnosticsEngine &Diag,
+                                const CodeGenOptions &Options) {
+  // Already checked in Sema, this is not a payload.
+  if (!Decl->hasAttr<HLSLRayPayloadAttr>())
+    return false;
+
+  // If we have a payload warn about them beeing dropped.
+  if (!Options.HLSLEnablePayloadAccessQualifiers) {
+    Diag.ReportOnce(Decl->getLocation(), diag::warn_hlsl_payload_qualifer_dropped);
+    return false;
+  }
+
+  // Check if all fileds have a payload qualifier.
+  bool allFieldsQualifed = true;
+  for (FieldDecl *field : Decl->fields()) {
+    bool fieldHasPayloadQualifier = false;
+    bool isPayloadStruct = false;
+    for (UnusualAnnotation *annotation : field->getUnusualAnnotations()) {
+      fieldHasPayloadQualifier |= isa<hlsl::PayloadAccessAnnotation>(annotation);
+    }
+    // Check if this is a struct type. 
+    // If it is, check for the [payload] field, [payload] structs must carry
+    // PayloadAccessQualifiers and these are taken from the struct directly. 
+    // If it is not a payload struct, check if it has qualifiers attached.
+    if (RecordDecl *recordTy = field->getType()->getAsCXXRecordDecl()) {
+      if (recordTy->hasAttr<HLSLRayPayloadAttr>())
+        isPayloadStruct = true;
+    }
+
+    if (fieldHasPayloadQualifier && isPayloadStruct) {
+      Diag.Report(field->getLocation(),
+                  diag::err_payload_fields_is_payload_and_overqualified)
+          << field->getName();
+      continue;
+    }
+    else 
+    {
+        if (isPayloadStruct)
+            fieldHasPayloadQualifier = true;
+    }
+
+    if (!fieldHasPayloadQualifier) {
+      Diag.Report(field->getLocation(),
+                  diag::err_payload_fields_not_qualified)
+          << field->getName();
+    }
+    allFieldsQualifed &= fieldHasPayloadQualifier;
+  }
+  if (!allFieldsQualifed) {
+    Diag.Report(Decl->getLocation(), diag::err_not_all_payload_fields_qualified)
+        << Decl->getName();
+    return false;
+  }
+ 
+  return true;
+}
+
 // Return the size for constant buffer of each decl.
 unsigned CGMSHLSLRuntime::AddTypeAnnotation(QualType Ty,
                                             DxilTypeSystem &dxilTypeSys,
@@ -1108,8 +1184,10 @@ unsigned CGMSHLSLRuntime::AddTypeAnnotation(QualType Ty,
     }
     DxilStructAnnotation *annotation = dxilTypeSys.AddStructAnnotation(ST,
       GetNumTemplateArgsForRecordDecl(RT->getDecl()));
-
-    return ConstructStructAnnotation(annotation, RD, dxilTypeSys);
+    DxilPayloadAnnotation *payloadAnnotation = nullptr;
+    if (ValidatePayloadDecl(RT->getDecl(), *m_pHLModule->GetShaderModel(), CGM.getDiags(), CGM.getCodeGenOpts()))
+      payloadAnnotation = dxilTypeSys.AddPayloadAnnotation(ST);
+    return ConstructStructAnnotation(annotation, payloadAnnotation, RD, dxilTypeSys);
   } else if (const RecordType *RT = dyn_cast<RecordType>(paramTy)) {
     // For this pointer.
     RecordDecl *RD = RT->getDecl();
@@ -1121,8 +1199,10 @@ unsigned CGMSHLSLRuntime::AddTypeAnnotation(QualType Ty,
     }
     DxilStructAnnotation *annotation = dxilTypeSys.AddStructAnnotation(ST,
       GetNumTemplateArgsForRecordDecl(RT->getDecl()));
-
-    return ConstructStructAnnotation(annotation, RD, dxilTypeSys);
+    DxilPayloadAnnotation* payloadAnnotation = nullptr;
+    if (ValidatePayloadDecl(RT->getDecl(), *m_pHLModule->GetShaderModel(), CGM.getDiags(), CGM.getCodeGenOpts()))
+         payloadAnnotation = dxilTypeSys.AddPayloadAnnotation(ST);
+    return ConstructStructAnnotation(annotation, payloadAnnotation, RD, dxilTypeSys);
   } else if (IsHLSLResourceType(Ty)) {
     // Save result type info.
     AddTypeAnnotation(GetHLSLResourceResultType(Ty), dxilTypeSys, arrayEltSize);
@@ -1731,6 +1811,7 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
   bool hasOutVertices = false;
   bool hasOutPrimitives = false;
   bool hasInPayload = false;
+  bool rayShaderHaveErrors = false;
   for (; ArgNo < F->arg_size(); ++ArgNo, ++ParmIdx, ++ArgIt) {
     DxilParameterAnnotation &paramAnnotation =
         FuncAnnotation->GetParameterAnnotation(ArgNo);
@@ -2071,27 +2152,31 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
           DiagnosticsEngine::Error, "parameters are not allowed for %0 shader"))
             << (funcProps->shaderKind == DXIL::ShaderKind::RayGeneration ?
                 "raygeneration" : "intersection");
-        break;
+        rayShaderHaveErrors = true;
       case DXIL::ShaderKind::AnyHit:
       case DXIL::ShaderKind::ClosestHit:
         if (0 == ArgNo && dxilInputQ != DxilParamInputQual::Inout) {
           Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
             DiagnosticsEngine::Error,
             "ray payload parameter must be inout"));
+          rayShaderHaveErrors = true;
         } else if (1 == ArgNo && dxilInputQ != DxilParamInputQual::In) {
           Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
             DiagnosticsEngine::Error,
             "intersection attributes parameter must be in"));
+          rayShaderHaveErrors = true;
         } else if (ArgNo > 1) {
           Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
             DiagnosticsEngine::Error,
             "too many parameters, expected payload and attributes parameters only."));
+          rayShaderHaveErrors = true;
         }
         if (ArgNo < 2) {
           if (!IsHLSLNumericUserDefinedType(parmDecl->getType())) {
             Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
               DiagnosticsEngine::Error,
               "payload and attribute structures must be user defined types with only numeric contents."));
+            rayShaderHaveErrors = true;
           } else {
             DataLayout DL(&this->TheModule);
             unsigned size = DL.getTypeAllocSize(F->getFunctionType()->getFunctionParamType(ArgNo)->getPointerElementType());
@@ -2107,16 +2192,19 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
           Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
             DiagnosticsEngine::Error,
             "only one parameter (ray payload) allowed for miss shader"));
+          rayShaderHaveErrors = true;
         } else if (dxilInputQ != DxilParamInputQual::Inout) {
           Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
             DiagnosticsEngine::Error,
             "ray payload parameter must be declared inout"));
+          rayShaderHaveErrors = true;
         }
         if (ArgNo < 1) {
           if (!IsHLSLNumericUserDefinedType(parmDecl->getType())) {
             Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
               DiagnosticsEngine::Error,
               "ray payload parameter must be a user defined type with only numeric contents."));
+            rayShaderHaveErrors = true;
           } else {
             DataLayout DL(&this->TheModule);
             unsigned size = DL.getTypeAllocSize(F->getFunctionType()->getFunctionParamType(ArgNo)->getPointerElementType());
@@ -2129,16 +2217,19 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
           Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
             DiagnosticsEngine::Error,
             "only one parameter allowed for callable shader"));
+          rayShaderHaveErrors = true;
         } else if (dxilInputQ != DxilParamInputQual::Inout) {
           Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
             DiagnosticsEngine::Error,
             "callable parameter must be declared inout"));
+          rayShaderHaveErrors = true;
         }
         if (ArgNo < 1) {
           if (!IsHLSLNumericUserDefinedType(parmDecl->getType())) {
             Diags.Report(parmDecl->getLocation(), Diags.getCustomDiagID(
               DiagnosticsEngine::Error,
               "callable parameter must be a user defined type with only numeric contents."));
+            rayShaderHaveErrors = true;
           } else {
             DataLayout DL(&this->TheModule);
             unsigned size = DL.getTypeAllocSize(F->getFunctionType()->getFunctionParamType(ArgNo)->getPointerElementType());
@@ -2188,6 +2279,7 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
           Diags.getCustomDiagID(DiagnosticsEngine::Error,
             "shader must include inout parameter structure.");
         Diags.Report(FD->getLocation(), DiagID);
+        rayShaderHaveErrors = true;
       }
     }
     if (bNeedsAttributes &&
@@ -2195,9 +2287,17 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
       Diags.Report(FD->getLocation(), Diags.getCustomDiagID(
         DiagnosticsEngine::Error,
         "shader must include attributes structure parameter."));
+      rayShaderHaveErrors = true;
     }
   }
 
+  // If we encountered an error during verification of RayTracing 
+  // shader signatures, stop here. Otherwise we risk to trigger 
+  // unhandled behaviour, i.e., DXC crashes when the payload is 
+  // declared as matrix<float...> type.
+  if(rayShaderHaveErrors)
+      return;
+
   // Type annotation for parameters and return type.
   DxilTypeSystem &dxilTypeSys = m_pHLModule->GetTypeSystem();
   unsigned arrayEltSize = 0;
@@ -2715,6 +2815,10 @@ static void InitFromUnusualAnnotations(DxilResourceBase &Resource, NamedDecl &De
     case hlsl::UnusualAnnotation::UA_ConstantPacking:
       // Should be handled by front-end
       llvm_unreachable("packoffset on resource");
+      break;    
+    case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier:
+      // Should be handled by front-end
+      llvm_unreachable("payload qualifier on resource");
       break;
     default:
       llvm_unreachable("unknown UnusualAnnotation on resource");
@@ -3273,6 +3377,9 @@ void CGMSHLSLRuntime::AddConstant(VarDecl *constDecl, HLCBuffer &CB) {
     }
     case hlsl::UnusualAnnotation::UA_SemanticDecl:
       // skip semantic on constant
+      break;    
+    case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier:
+      // skip payload qualifers on constant
       break;
     }
   }

+ 68 - 3
tools/clang/lib/Parse/ParseDecl.cpp

@@ -27,8 +27,10 @@
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringSwitch.h"
-#include "dxc/Support/Global.h"    // HLSL Change
-#include "clang/Sema/SemaHLSL.h"   // HLSL Change
+#include "dxc/Support/Global.h"       // HLSL Change
+#include "clang/Sema/SemaHLSL.h"      // HLSL Change
+#include "dxc/DXIL/DxilShaderModel.h" // HLSL Change
+#include "dxc/DXIL/DxilConstants.h"   // HLSL Change
 
 using namespace clang;
 
@@ -359,7 +361,70 @@ bool Parser::MaybeParseHLSLAttributes(std::vector<hlsl::UnusualAnnotation *> &ta
       return false;
     }
 
-    if (NextToken().is(tok::kw_register)) {
+    bool identifierIsPayloadAnnotation = false;
+    if (NextToken().is(tok::identifier)) {
+        StringRef identifier = NextToken().getIdentifierInfo()->getName();
+        identifierIsPayloadAnnotation = identifier == "read" || identifier == "write";
+    }
+
+    if (identifierIsPayloadAnnotation) {
+      hlsl::PayloadAccessAnnotation mod;
+
+      if (NextToken().getIdentifierInfo()->getName() == "read")
+          mod.qualifier = hlsl::DXIL::PayloadAccessQualifier::Read;
+      else
+          mod.qualifier = hlsl::DXIL::PayloadAccessQualifier::Write;
+
+      // : read/write ( shader stage *[,shader stage])
+      ConsumeToken(); // consume the colon.
+
+      mod.Loc = Tok.getLocation();
+      ConsumeToken(); // consume the read/write identifier
+      if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
+                           "payload access qualifier")) {
+        return true;
+      }
+
+      while(Tok.is(tok::identifier)) {
+        hlsl::DXIL::PayloadAccessShaderStage stage = hlsl::DXIL::PayloadAccessShaderStage::Invalid;
+        const char *stagePtr = Tok.getIdentifierInfo()->getName().data();
+        StringRef shaderStage(stagePtr);
+        if (shaderStage != "caller" && shaderStage != "anyhit" &&
+            shaderStage != "closesthit" && shaderStage != "miss") {
+          Diag(Tok.getLocation(),
+               diag::err_hlsl_payload_access_qualifier_unsupported_shader)
+              << shaderStage;
+          return true;
+        }
+
+        if (shaderStage == "caller") {
+          stage = hlsl::DXIL::PayloadAccessShaderStage::Caller;
+        } else if (shaderStage == "closesthit") {
+          stage = hlsl::DXIL::PayloadAccessShaderStage::Closesthit;
+        } else if (shaderStage == "miss") {
+          stage = hlsl::DXIL::PayloadAccessShaderStage::Miss;
+        } else if (shaderStage == "anyhit") {
+          stage = hlsl::DXIL::PayloadAccessShaderStage::Anyhit;
+        } 
+
+        mod.ShaderStages.push_back(stage);
+        ConsumeToken(); // consume shader type
+
+        if (Tok.is(tok::comma)) // check if we have a list of shader types
+          ConsumeToken();
+
+      } while (Tok.is(tok::identifier));
+
+      if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen_after,
+                           "payload access qualifier")) {
+        return true;
+      }
+
+      if (mod.ShaderStages.empty())
+          mod.qualifier = hlsl::DXIL::PayloadAccessQualifier::NoAccess;
+
+      target.push_back(new (context) hlsl::PayloadAccessAnnotation(mod));
+    }else if (NextToken().is(tok::kw_register)) {
       hlsl::RegisterAssignment r;
 
       // : register ([shader_profile], Type#[subcomponent] [,spaceX])

+ 1 - 0
tools/clang/lib/Sema/CMakeLists.txt

@@ -27,6 +27,7 @@ add_clang_library(clangSema
   SemaDeclAttr.cpp
   SemaDeclCXX.cpp
   SemaDeclObjC.cpp
+  SemaDXR.cpp
   SemaExceptionSpec.cpp
   SemaExpr.cpp
   SemaExprCXX.cpp

+ 1119 - 0
tools/clang/lib/Sema/SemaDXR.cpp

@@ -0,0 +1,1119 @@
+//===------ SemaDXR.cpp - Semantic Analysis for DXR shader -----*- C++ -*-===//
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// SemaDXR.cpp                                                               //
+// Copyright (C) Nvidia Corporation. All rights reserved.                    //
+// This file is distributed under the University of Illinois Open Source     //
+// License. See LICENSE.TXT for details.                                     //
+//                                                                           //
+//  This file defines the semantic support for DXR.                          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExternalASTSource.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Sema/SemaHLSL.h"
+
+#include "clang/Analysis/Analyses/Dominators.h"
+#include "clang/Analysis/Analyses/ReachableCode.h"
+#include "clang/Analysis/CFG.h"
+
+#include "llvm/ADT/BitVector.h"
+
+#include "dxc/DXIL/DxilConstants.h"
+
+using namespace clang;
+using namespace sema;
+using namespace hlsl;
+
+namespace {
+
+struct PayloadUse {
+  PayloadUse() = default;
+  PayloadUse(const Stmt *S, const CFGBlock *Parent)
+      : S(S), Parent(Parent), Member(nullptr) {}
+  PayloadUse(const Stmt *S, const CFGBlock *Parent, const MemberExpr *Member)
+      : S(S), Parent(Parent), Member(Member) {}
+
+  bool operator<(const PayloadUse &Other) const { return S < Other.S; }
+
+  const Stmt *S = nullptr;
+  const CFGBlock *Parent = nullptr;
+  const MemberExpr *Member = nullptr;
+};
+
+struct TraceRayCall {
+  TraceRayCall() = default;
+  TraceRayCall(const CallExpr *Call, const CFGBlock *Parent)
+      : Call(Call), Parent(Parent) {}
+  const CallExpr *Call = nullptr;
+  const CFGBlock *Parent = nullptr;
+};
+
+struct PayloadAccessInfo {
+  PayloadAccessInfo() = default;
+  PayloadAccessInfo(const MemberExpr *Member, const CallExpr *Call,
+                    bool IsLValue)
+      : Member(Member), Call(Call), IsLValue(IsLValue) {}
+  const MemberExpr *Member = nullptr;
+  const CallExpr *Call = nullptr;
+  bool IsLValue = false;
+};
+
+struct DxrShaderDiagnoseInfo {
+  const FunctionDecl *FunctionDecl;
+  const VarDecl *Payload;
+  DXIL::PayloadAccessShaderStage Stage;
+  std::vector<TraceRayCall> TraceCalls;
+  std::map<const FieldDecl *, std::vector<PayloadUse>> WritesPerField;
+  std::map<const FieldDecl *, std::vector<PayloadUse>> ReadsPerField;
+  std::vector<PayloadUse> PayloadAsCallArg;
+};
+
+std::vector<const FieldDecl *>
+DiagnosePayloadAccess(Sema &S, DxrShaderDiagnoseInfo &Info,
+                      const std::set<const FieldDecl *> &FieldsToIgnoreRead,
+                      const std::set<const FieldDecl *> &FieldsToIgnoreWrite,
+                      std::set<const FunctionDecl *> VisitedFunctions);
+
+const Stmt *IgnoreParensAndDecay(const Stmt *S);
+
+// Transform the shader stage to string to be used in diagnostics
+StringRef GetStringForShaderStage(DXIL::PayloadAccessShaderStage Stage) {
+  StringRef StageNames[] = {"caller", "closesthit", "miss", "anyhit"};
+  if (Stage != DXIL::PayloadAccessShaderStage::Invalid)
+    return StageNames[static_cast<unsigned>(Stage)];
+  return "";
+}
+
+// Returns the Qualifier for a Field and a given shader stage.
+DXIL::PayloadAccessQualifier
+GetPayloadQualifierForStage(FieldDecl *Field,
+                            DXIL::PayloadAccessShaderStage Stage) {
+  bool hasRead = false;
+  bool hasWrite = false;
+  for (UnusualAnnotation *annotation : Field->getUnusualAnnotations()) {
+    if (auto *payloadAnnotation =
+            dyn_cast<hlsl::PayloadAccessAnnotation>(annotation)) {
+      for (auto &ShaderStage : payloadAnnotation->ShaderStages) {
+        if (ShaderStage != Stage)
+          continue;
+        hasRead |=
+            payloadAnnotation->qualifier == DXIL::PayloadAccessQualifier::Read;
+        hasWrite |=
+            payloadAnnotation->qualifier == DXIL::PayloadAccessQualifier::Write;
+      }
+    }
+  }
+  if (hasRead && hasWrite)
+    return DXIL::PayloadAccessQualifier::ReadWrite;
+  if (hasRead)
+    return DXIL::PayloadAccessQualifier::Read;
+  if (hasWrite)
+    return DXIL::PayloadAccessQualifier::Write;
+  return DXIL::PayloadAccessQualifier::NoAccess;
+}
+
+// Returns the declaration of the payload used in a TraceRay call
+const VarDecl *GetPayloadParameterForTraceCall(const CallExpr *Trace) {
+  const Decl *callee = Trace->getCalleeDecl();
+  if (!callee)
+    return nullptr;
+
+  if (!isa<FunctionDecl>(callee))
+    return nullptr;
+
+  const FunctionDecl *FD = cast<FunctionDecl>(callee);
+
+  if (FD->isImplicit() && FD->getName() == "TraceRay") {
+    const Stmt *Param = IgnoreParensAndDecay(Trace->getArg(7));
+    if (const DeclRefExpr *ParamRef = dyn_cast<DeclRefExpr>(Param)) {
+      if (const VarDecl *Decl = dyn_cast<VarDecl>(ParamRef->getDecl()))
+        return Decl;
+    }
+  }
+  return nullptr;
+}
+
+// Recursively extracts accesses to a payload struct from a Stmt
+void GetPayloadAccesses(const Stmt *S, const DxrShaderDiagnoseInfo &Info,
+                        std::vector<PayloadAccessInfo> &Accesses, bool IsLValue,
+                        const MemberExpr *Member, const CallExpr *Call) {
+  for (auto C : S->children()) {
+    if (!C)
+      continue;
+    if (const DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(C)) {
+      if (Ref->getDecl() == Info.Payload) {
+        Accesses.push_back(PayloadAccessInfo{Member, Call, IsLValue});
+      }
+      return;
+    }
+    if (const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(C)) {
+      if (Cast->getCastKind() == CK_LValueToRValue) {
+        IsLValue = false;
+      }
+    }
+
+    GetPayloadAccesses(C, Info, Accesses, IsLValue,
+                       Member ? Member : dyn_cast<MemberExpr>(C),
+                       Call ? Call : dyn_cast<CallExpr>(C));
+  }
+}
+
+// Collects all reads, writes and calls with participation of the payload.
+void CollectReadsWritesAndCallsForPayload(const Stmt *S,
+                                          DxrShaderDiagnoseInfo &Info,
+                                          const CFGBlock *Block) {
+  std::vector<PayloadAccessInfo> PayloadAccesses;
+  GetPayloadAccesses(S, Info, PayloadAccesses, true, dyn_cast<MemberExpr>(S),
+                     dyn_cast<CallExpr>(S));
+  for (auto &Access : PayloadAccesses) {
+    // An access to a payload member was found.
+    if (Access.Member) {
+      FieldDecl *Field = cast<FieldDecl>(Access.Member->getMemberDecl());
+      if (Access.IsLValue) {
+        Info.WritesPerField[Field].push_back(
+            PayloadUse{S, Block, Access.Member});
+      } else {
+        Info.ReadsPerField[Field].push_back(PayloadUse{S, Block, Access.Member});
+      }
+    } else if (Access.Call) {
+      Info.PayloadAsCallArg.push_back(PayloadUse{S, Block});
+    }
+  }
+}
+
+// Collects all TraceRay calls.
+void CollectTraceRayCalls(const Stmt *S, DxrShaderDiagnoseInfo &Info,
+                          const CFGBlock *Block) {
+  // TraceRay has void as return type so it should never be something else
+  // than a plain CallExpr.
+
+  if (const CallExpr *Call = dyn_cast<CallExpr>(S)) {
+
+    const Decl *Callee = Call->getCalleeDecl();
+    if (!Callee || !isa<FunctionDecl>(Callee))
+      return;
+
+    const FunctionDecl *CalledFunction = cast<FunctionDecl>(Callee);
+
+    // Ignore trace calls here.
+    if (CalledFunction->isImplicit() &&
+        CalledFunction->getName() == "TraceRay") {
+      Info.TraceCalls.push_back({Call, Block});
+    }
+  }
+}
+
+// Find the last write to the payload field in the given block.
+PayloadUse GetLastWriteInBlock(CFGBlock &Block,
+                              ArrayRef<PayloadUse> PayloadWrites) {
+  PayloadUse LastWrite;
+  for (auto &Element : Block) { // TODO: reverse iterate?
+    if (Optional<CFGStmt> S = Element.getAs<CFGStmt>()) {
+      auto It =
+          std::find_if(PayloadWrites.begin(), PayloadWrites.end(),
+                       [&](const PayloadUse &V) { return V.S == S->getStmt(); });
+      if (It != std::end(PayloadWrites)) {
+        LastWrite = *It;
+        LastWrite.Parent = &Block;
+      }
+    }
+  }
+  return LastWrite;
+}
+
+// Travers the CFG until every path has reached a write or the ENTRY.
+void TraverseCFGUntilWrite(CFGBlock &Current, std::vector<PayloadUse> &Writes,
+                           ArrayRef<PayloadUse> PayloadWrites,
+                           std::set<const CFGBlock *> &Visited) {
+
+  if (Visited.count(&Current))
+    return;
+  Visited.insert(&Current);
+
+  for (auto I = Current.pred_begin(), E = Current.pred_end(); I != E; ++I) {
+    CFGBlock *Pred = *I;
+    if (!Pred)
+      continue;
+    PayloadUse WriteInPred = GetLastWriteInBlock(*Pred, PayloadWrites);
+    if (!WriteInPred.S)
+      TraverseCFGUntilWrite(*Pred, Writes, PayloadWrites, Visited);
+    else
+      Writes.push_back(WriteInPred);
+  }
+}
+
+// Traverse the CFG from the EXIT backwards and stop as soon as a block has a
+// write to the payload field.
+std::vector<PayloadUse>
+GetAllWritesReachingExit(CFG &ShaderCFG, ArrayRef<PayloadUse> PayloadWrites) {
+
+  std::vector<PayloadUse> Writes;
+  CFGBlock &Exit = ShaderCFG.getExit();
+
+  std::set<const CFGBlock *> Visited;
+  TraverseCFGUntilWrite(Exit, Writes, PayloadWrites, Visited);
+
+  return Writes;
+}
+
+// Find the first read to the payload field in the given block.
+PayloadUse GetFirstReadInBlock(CFGBlock &Block,
+                              ArrayRef<PayloadUse> PayloadReads) {
+  PayloadUse FirstRead;
+  for (auto &Element : Block) {
+    if (Optional<CFGStmt> S = Element.getAs<CFGStmt>()) {
+      auto It =
+          std::find_if(PayloadReads.begin(), PayloadReads.end(),
+                       [&](const PayloadUse &V) { return V.S == S->getStmt(); });
+      if (It != std::end(PayloadReads)) {
+        FirstRead = *It;
+        FirstRead.Parent = &Block;
+        break; // We found the first read and are done with this block.
+      }
+    }
+  }
+  return FirstRead;
+}
+
+// Travers the CFG until every path has reached a read or the EXIT.
+void TraverseCFGUntilRead(CFGBlock &Current, std::vector<PayloadUse> &Reads,
+                          ArrayRef<PayloadUse> PayloadWrites,
+                          std::set<const CFGBlock *> &Visited) {
+
+  if (Visited.count(&Current))
+    return;
+  Visited.insert(&Current);
+
+  for (auto I = Current.succ_begin(), E = Current.succ_end(); I != E; ++I) {
+    CFGBlock *Succ = *I;
+    if (!Succ)
+      continue;
+    PayloadUse ReadInSucc = GetFirstReadInBlock(*Succ, PayloadWrites);
+    if (!ReadInSucc.S)
+      TraverseCFGUntilRead(*Succ, Reads, PayloadWrites, Visited);
+    else
+      Reads.push_back(ReadInSucc);
+  }
+}
+
+// Traverse the CFG from the ENTRY down and stop as soon as a block has a read
+// to the payload field.
+std::vector<PayloadUse>
+GetAllReadsReachedFromEntry(CFG &ShaderCFG, ArrayRef<PayloadUse> PayloadReads) {
+  std::vector<PayloadUse> Reads;
+  CFGBlock &Entry = ShaderCFG.getEntry();
+
+  std::set<const CFGBlock *> Visited;
+  TraverseCFGUntilRead(Entry, Reads, PayloadReads, Visited);
+
+  return Reads;
+}
+
+// Returns the record type of a payload declaration.
+CXXRecordDecl *GetPayloadType(const VarDecl *Payload) {
+  auto PayloadType = Payload->getType();
+  if (PayloadType->isStructureOrClassType()) {
+    return PayloadType->getAsCXXRecordDecl();
+  }
+  return nullptr;
+}
+
+std::vector<FieldDecl*> GetAllPayloadFields(RecordDecl* PayloadType) {
+  std::vector<FieldDecl*> PayloadFields;
+
+  for (FieldDecl *Field : PayloadType->fields()) {
+    QualType FieldType = Field->getType();
+    if (RecordDecl *FieldRecordDecl = FieldType->getAsCXXRecordDecl()) {
+      // Skip nested payload types.
+      if (FieldRecordDecl->hasAttr<HLSLRayPayloadAttr>()) {
+        auto SubTypeFields = GetAllPayloadFields(FieldRecordDecl);
+        PayloadFields.insert(PayloadFields.end(), SubTypeFields.begin(), SubTypeFields.end());
+        continue;
+      }
+    }
+    PayloadFields.push_back(Field);
+  }
+
+  return PayloadFields;
+}
+
+// Returns true if the field is writeable in an earlier shader stage.
+bool IsFieldWriteableInEarlierStage(FieldDecl *Field,
+                                    DXIL::PayloadAccessShaderStage ThisStage) {
+  bool isWriteableInEarlierStage = false;
+  switch (ThisStage) {
+  case DXIL::PayloadAccessShaderStage::Anyhit:
+  case DXIL::PayloadAccessShaderStage::Closesthit:
+  case DXIL::PayloadAccessShaderStage::Miss: {
+    auto Qualifier = GetPayloadQualifierForStage(
+        Field, DXIL::PayloadAccessShaderStage::Caller);
+    isWriteableInEarlierStage =
+        Qualifier == DXIL::PayloadAccessQualifier::Write ||
+        Qualifier == DXIL::PayloadAccessQualifier::ReadWrite;
+    Qualifier = GetPayloadQualifierForStage(
+        Field, DXIL::PayloadAccessShaderStage::Anyhit);
+    isWriteableInEarlierStage |=
+        Qualifier == DXIL::PayloadAccessQualifier::Write ||
+        Qualifier == DXIL::PayloadAccessQualifier::ReadWrite;
+  } break;
+  default:
+    break;
+  }
+  return isWriteableInEarlierStage;
+}
+
+// Emit warnings on payload writes.
+void DiagnosePayloadWrites(Sema &S, CFG &ShaderCFG, DominatorTree &DT,
+                           const DxrShaderDiagnoseInfo &Info,
+                           ArrayRef<FieldDecl *> NonWriteableFields,
+                           RecordDecl *PayloadType) {
+  for (FieldDecl *Field : NonWriteableFields) {
+    auto WritesToField = Info.WritesPerField.find(Field);
+    if (WritesToField == Info.WritesPerField.end())
+      continue;
+
+    const auto &WritesToDiagnose =
+        GetAllWritesReachingExit(ShaderCFG, WritesToField->second);
+    for (auto &Write : WritesToDiagnose) {
+      FieldDecl *MemField = cast<FieldDecl>(Write.Member->getMemberDecl());
+      auto Qualifier = GetPayloadQualifierForStage(MemField, Info.Stage);
+      if (Qualifier != DXIL::PayloadAccessQualifier::Write &&
+          Qualifier != DXIL::PayloadAccessQualifier::ReadWrite) {
+        S.Diag(Write.Member->getExprLoc(), diag::warn_hlsl_payload_access_write_loss)
+            << Field->getName() << GetStringForShaderStage(Info.Stage);
+      }
+    }
+  }
+
+  // Check if a field is not unconditionally written and a write form an earlier
+  // stage will be lost.
+  auto PayloadFields = GetAllPayloadFields(PayloadType);
+  for (FieldDecl *Field : PayloadFields) {
+    auto Qualifier = GetPayloadQualifierForStage(Field, Info.Stage);
+    if (IsFieldWriteableInEarlierStage(Field, Info.Stage) &&
+        Qualifier == DXIL::PayloadAccessQualifier::Write) {
+
+      // The field is writeable in an earlier stage and pure write in this
+      // stage. Check if we find a write that dominates the exit of the
+      // function.
+      bool fieldHasDominatingWrite = false;
+      auto It = Info.WritesPerField.find(Field);
+      if (It != Info.WritesPerField.end()) {
+        for (auto &Write : It->second) {
+          fieldHasDominatingWrite =
+              DT.dominates(Write.Parent, &ShaderCFG.getExit());
+          if (fieldHasDominatingWrite)
+            break;
+        }
+      }
+
+      if (!fieldHasDominatingWrite) {
+        S.Diag(Info.Payload->getLocation(),
+               diag::warn_hlsl_payload_access_data_loss)
+            << Field->getName() << GetStringForShaderStage(Info.Stage);
+      }
+    }
+  }
+}
+
+// Returns true if A is earlier than B in Parent
+bool IsEarlierStatementAs(const Stmt *A, const Stmt *B,
+                          const CFGBlock &Parent) {
+  for (auto Element : Parent) {
+    if (auto S = Element.getAs<CFGStmt>()) {
+      if (S->getStmt() == A)
+        return true;
+      if (S->getStmt() == B)
+        return false;
+    }
+  }
+  return true;
+}
+
+// Returns true if the write dominates payload use.
+template <typename T>
+bool WriteDominatesUse(const PayloadUse &Write, const T &Use,
+                       DominatorTree &DT) {
+  if (Use.Parent == Write.Parent) {
+    // Use and write are in the same Block.
+    return IsEarlierStatementAs(Write.S, Use.S, *Use.Parent);
+  }
+
+  return DT.dominates(Write.Parent, Use.Parent);
+}
+
+// Emit warnings for payload reads.
+void DiagnosePayloadReads(Sema &S, CFG &ShaderCFG, DominatorTree &DT,
+                          const DxrShaderDiagnoseInfo &Info,
+                          ArrayRef<FieldDecl *> NonReadableFields) {
+  for (FieldDecl *Field : NonReadableFields) {
+    auto ReadsFromField = Info.ReadsPerField.find(Field);
+    if (ReadsFromField == Info.ReadsPerField.end())
+      continue;
+
+    auto WritesToField = Info.WritesPerField.find(Field);
+    bool FieldHasWrites = WritesToField != Info.WritesPerField.end();
+
+    const auto &ReadsToDiagnose =
+        GetAllReadsReachedFromEntry(ShaderCFG, ReadsFromField->second);
+
+    for (auto &Read : ReadsToDiagnose) {
+      bool ReadIsDominatedByWrite = false;
+      if (FieldHasWrites) {
+        // We found a read to a field that needs diagnose.
+        // We do not want to warn about fields that read but are dominated by a
+        // write. Find writes that dominate the read. If we found one, ignore
+        // the read.
+        for (auto Write : WritesToField->second) {
+          ReadIsDominatedByWrite = WriteDominatesUse(Write, Read, DT);
+          if (ReadIsDominatedByWrite)
+            break;
+        }
+      }
+
+      if (ReadIsDominatedByWrite)
+        continue;
+
+      FieldDecl *MemField = cast<FieldDecl>(Read.Member->getMemberDecl());
+
+      auto Qualifier = GetPayloadQualifierForStage(MemField, Info.Stage);
+      if (Qualifier != DXIL::PayloadAccessQualifier::Read &&
+          Qualifier != DXIL::PayloadAccessQualifier::ReadWrite) {
+        S.Diag(Read.Member->getExprLoc(), diag::warn_hlsl_payload_access_undef_read)
+            << Field->getName() << GetStringForShaderStage(Info.Stage);
+      }
+    }
+  }
+}
+
+// Generic CFG traversal that performs PerElementAction on every Stmt in the
+// CFG.
+template <bool Backward, typename Action>
+void TraverseCFG(const CFGBlock &Block, Action PerElementAction,
+                 std::set<const CFGBlock *> &Visited) {
+  if (Visited.count(&Block))
+    return;
+  Visited.insert(&Block);
+
+  for (const auto &Element : Block) {
+    PerElementAction(Block, Element);
+  }
+
+  if (!Backward) {
+    for (auto I = Block.succ_begin(), E = Block.succ_end(); I != E; ++I) {
+      CFGBlock *Succ = *I;
+      if (!Succ)
+        continue;
+      TraverseCFG</*Backward=*/false>(*Succ, PerElementAction, Visited);
+    }
+  } else {
+    for (auto I = Block.pred_begin(), E = Block.pred_end(); I != E; ++I) {
+      CFGBlock *Pred = *I;
+      if (!Pred)
+        continue;
+      TraverseCFG<Backward>(*Pred, PerElementAction, Visited);
+    }
+  }
+}
+
+// Forward traverse the CFG and collect calls to TraceRay.
+void ForwardTraverseCFGAndCollectTraceCalls(
+    const CFGBlock &Block, DxrShaderDiagnoseInfo &Info,
+    std::set<const CFGBlock *> &Visited) {
+  auto Action = [&Info](const CFGBlock &Block, const CFGElement &Element) {
+    if (Optional<CFGStmt> S = Element.getAs<CFGStmt>()) {
+      CollectTraceRayCalls(S->getStmt(), Info, &Block);
+    }
+  };
+
+  TraverseCFG<false>(Block, Action, Visited);
+}
+
+// Foward traverse the CFG and collect all reads and writes to the payload.
+void ForwardTraverseCFGAndCollectReadsWrites(
+    const CFGBlock &StartBlock, DxrShaderDiagnoseInfo &Info,
+    std::set<const CFGBlock *> &Visited) {
+  auto Action = [&Info](const CFGBlock &Block, const CFGElement &Element) {
+    if (Optional<CFGStmt> S = Element.getAs<CFGStmt>()) {
+      CollectReadsWritesAndCallsForPayload(S->getStmt(), Info, &Block);
+    }
+  };
+
+  TraverseCFG<false>(StartBlock, Action, Visited);
+}
+
+// Backward traverse the CFG and collect all reads and writes to the payload.
+void BackwardTraverseCFGAndCollectReadsWrites(
+    const CFGBlock &StartBlock, DxrShaderDiagnoseInfo &Info,
+    std::set<const CFGBlock *> &Visited) {
+  auto Action = [&](const CFGBlock &Block, const CFGElement &Element) {
+    if (Optional<CFGStmt> S = Element.getAs<CFGStmt>()) {
+      CollectReadsWritesAndCallsForPayload(S->getStmt(), Info, &Block);
+    }
+  };
+
+  TraverseCFG<true>(StartBlock, Action, Visited);
+}
+
+// Returns true if the Stmt uses the Payload.
+bool IsPayloadArg(const Stmt *S, const Decl *Payload) {
+  if (const DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(S)) {
+    const Decl *Decl = Ref->getDecl();
+    if (Decl == Payload)
+      return true;
+  }
+
+  for (auto C : S->children()) {
+    if (IsPayloadArg(C, Payload))
+      return true;
+  }
+  return false;
+}
+
+bool DiagnoseCallExprForExternal(Sema &S, const FunctionDecl *FD,
+                                 const CallExpr *CE,
+                                 const ParmVarDecl *Payload);
+
+// Collects all writes that dominate a PayloadUse in a CallExpr
+// and returns a set of the Fields accessed.
+std::set<const FieldDecl *>
+CollectDominatingWritesForCall(PayloadUse &Use, DxrShaderDiagnoseInfo &Info,
+                               DominatorTree &DT) {
+  std::set<const FieldDecl *> FieldsToIgnore;
+
+  for (auto P : Info.WritesPerField) {
+    for (auto Write : P.second) {
+      bool WriteDominatesCallSite = WriteDominatesUse(Write, Use, DT);
+      if (WriteDominatesCallSite) {
+        FieldsToIgnore.insert(P.first);
+        break;
+      }
+    }
+  }
+
+  return FieldsToIgnore;
+}
+
+// Collects all reads that are reachable from a PayloadUse in a CallExpr
+// and returns a set of the Fields accessed.
+std::set<const FieldDecl *>
+CollectReachableWritesForCall(PayloadUse &Use,
+                              const DxrShaderDiagnoseInfo &Info) {
+  std::set<const FieldDecl *> FieldsToIgnore;
+  assert(Use.Parent);
+  const CFGBlock *Current = Use.Parent;
+
+  // Traverse the CFG beginning from the block of the call and collect all
+  // fields written to after the call. These fields must not be diagnosed with
+  // warnings about lost writes.
+  DxrShaderDiagnoseInfo TempInfo;
+  TempInfo.Payload = Info.Payload;
+  bool foundCall = false;
+  for (auto &Element : *Current) {
+    // Search for the Call in the block
+    if (Optional<CFGStmt> S = Element.getAs<CFGStmt>()) {
+      if (S->getStmt() == Use.S) {
+        foundCall = true;
+        continue;
+      }
+      if (foundCall)
+        CollectReadsWritesAndCallsForPayload(S->getStmt(), TempInfo, Current);
+    }
+  }
+
+  for (auto I = Current->succ_begin(); I != Current->succ_end(); ++I) {
+    CFGBlock *Succ = *I;
+    if (!Succ)
+      continue;
+    std::set<const CFGBlock *> Visited;
+    ForwardTraverseCFGAndCollectReadsWrites(*Succ, TempInfo, Visited);
+  }
+  for (auto &p : TempInfo.WritesPerField)
+    FieldsToIgnore.insert(p.first);
+
+  return FieldsToIgnore;
+}
+
+// Emit diagnostics when the payload is used as an argument
+// in a function call.
+std::map<PayloadUse, std::vector<const FieldDecl *>>
+DiagnosePayloadAsFunctionArg(
+    Sema &S, DxrShaderDiagnoseInfo &Info, DominatorTree &DT,
+    const std::set<const FieldDecl *> &ParentFieldsToIgnoreRead,
+    const std::set<const FieldDecl *> &ParentFieldsToIgnoreWrite,
+    std::set<const FunctionDecl *> VisitedFunctions) {
+  std::map<PayloadUse, std::vector<const FieldDecl *>> WrittenFieldsInCalls;
+  for (PayloadUse &Use : Info.PayloadAsCallArg) {
+    if (const CallExpr *Call = dyn_cast<CallExpr>(Use.S)) {
+      const Decl *Callee = Call->getCalleeDecl();
+      if (!Callee || !isa<FunctionDecl>(Callee))
+        continue;
+
+      const FunctionDecl *CalledFunction = cast<FunctionDecl>(Callee);
+
+      // Ignore trace calls here.
+      if (CalledFunction->isImplicit() &&
+          CalledFunction->getName() == "TraceRay") {
+        Info.TraceCalls.push_back(TraceRayCall{Call, Use.Parent});
+        continue;
+      }
+
+      // Handle external function calls
+      if (!CalledFunction->hasBody()) {
+        assert(isa<ParmVarDecl>(Info.Payload));
+        DiagnoseCallExprForExternal(S, CalledFunction, Call,
+                                    cast<ParmVarDecl>(Info.Payload));
+        continue;
+      }
+
+      if (VisitedFunctions.count(CalledFunction))
+        return WrittenFieldsInCalls;
+      VisitedFunctions.insert(CalledFunction);
+
+      DxrShaderDiagnoseInfo CalleeInfo;
+
+      for (unsigned i = 0; i < Call->getNumArgs(); ++i) {
+        const Expr *Arg = Call->getArg(i);
+        if (IsPayloadArg(Arg, Info.Payload)) {
+          CalleeInfo.Payload = CalledFunction->getParamDecl(i);
+          break;
+        }
+      }
+
+      if (CalleeInfo.Payload) {
+        CalleeInfo.FunctionDecl = CalledFunction;
+        CalleeInfo.Stage = Info.Stage;
+        auto FieldsToIgnoreRead = CollectDominatingWritesForCall(Use, Info, DT);
+        auto FieldsToIgnoreWrite = CollectReachableWritesForCall(Use, Info);
+        FieldsToIgnoreRead.insert(ParentFieldsToIgnoreRead.begin(),
+                                  ParentFieldsToIgnoreRead.end());
+        FieldsToIgnoreWrite.insert(ParentFieldsToIgnoreWrite.begin(),
+                                   ParentFieldsToIgnoreWrite.end());
+        WrittenFieldsInCalls[Use] =
+            DiagnosePayloadAccess(S, CalleeInfo, FieldsToIgnoreRead,
+                                  FieldsToIgnoreWrite, VisitedFunctions);
+      }
+    }
+  }
+  return WrittenFieldsInCalls;
+}
+
+// Collect all fields that cannot be accessed for the given shader stage. 
+// This function recurses into nested payload types.
+void CollectNonAccessableFields(
+    RecordDecl *PayloadType, DXIL::PayloadAccessShaderStage Stage,
+    const std::set<const FieldDecl *> &FieldsToIgnoreRead,
+    const std::set<const FieldDecl *> &FieldsToIgnoreWrite,
+    std::vector<FieldDecl *> &NonWriteableFields,
+    std::vector<FieldDecl *> &NonReadableFields) {
+  for (FieldDecl *Field : PayloadType->fields()) {
+    QualType FieldType = Field->getType();
+    if (RecordDecl *FieldRecordDecl = FieldType->getAsCXXRecordDecl()) {
+      if (FieldRecordDecl->hasAttr<HLSLRayPayloadAttr>()) {
+        CollectNonAccessableFields(FieldRecordDecl, Stage, FieldsToIgnoreRead,
+                                   FieldsToIgnoreWrite, NonWriteableFields,
+                                   NonReadableFields);
+        continue;
+      }
+    }
+
+    auto Qualifier = GetPayloadQualifierForStage(Field, Stage);
+    // Diagnose writes only if they are not written heigher in the call-graph.
+    if (!FieldsToIgnoreWrite.count(Field)) {
+      if (Qualifier != DXIL::PayloadAccessQualifier::Write &&
+          Qualifier != DXIL::PayloadAccessQualifier::ReadWrite)
+        NonWriteableFields.push_back(Field);
+    }
+    // Diagnose reads only if they have no write heigher in the call-graph.
+    if (!FieldsToIgnoreRead.count(Field)) {
+      if (Qualifier != DXIL::PayloadAccessQualifier::Read &&
+          Qualifier != DXIL::PayloadAccessQualifier::ReadWrite)
+        NonReadableFields.push_back(Field);
+    }
+  }
+}
+
+void CollectAccessableFields(RecordDecl *PayloadType,
+                             const std::vector<FieldDecl *> &NonWriteableFields,
+                             const std::vector<FieldDecl *> &NonReadableFields,
+                             std::vector<FieldDecl *> &WriteableFields,
+                             std::vector<FieldDecl *> &ReadableFields) {
+  for (FieldDecl *Field : PayloadType->fields()) {
+    QualType FieldType = Field->getType();
+    if (RecordDecl *FieldRecordDecl = FieldType->getAsCXXRecordDecl()) {
+      // Skip nested payload types.
+      if (FieldRecordDecl->hasAttr<HLSLRayPayloadAttr>()) {
+        CollectAccessableFields(FieldRecordDecl, NonWriteableFields,
+                                NonReadableFields, WriteableFields,
+                                ReadableFields);
+        continue;
+      }
+    }
+
+    if (std::find(NonWriteableFields.begin(), NonWriteableFields.end(),
+                  Field) == NonWriteableFields.end())
+      WriteableFields.push_back(Field);
+
+    if (std::find(NonReadableFields.begin(), NonReadableFields.end(), Field) ==
+        NonReadableFields.end())
+      ReadableFields.push_back(Field);
+  }
+}
+
+// Emit diagnostics for a TraceRay call.
+void DiagnoseTraceCall(Sema &S, const VarDecl *Payload,
+                       const TraceRayCall &Trace, DominatorTree &DT) {
+  // For each TraceRay call check if write(caller) fields are written.
+  const DXIL::PayloadAccessShaderStage CallerStage =
+      DXIL::PayloadAccessShaderStage::Caller;
+
+  std::vector<FieldDecl *> WriteableFields;
+  std::vector<FieldDecl *> NonWriteableFields;
+  std::vector<FieldDecl *> ReadableFields;
+  std::vector<FieldDecl *> NonReadableFields;
+
+  RecordDecl *PayloadType = GetPayloadType(Payload);
+
+  // Check if the payload type used for this trace call is a payload type
+  if (!PayloadType->hasAttr<HLSLRayPayloadAttr>()) {
+    S.Diag(Payload->getLocation(), diag::err_payload_requires_attribute)
+        << PayloadType->getName();
+    return;
+  }
+
+  CollectNonAccessableFields(PayloadType, CallerStage, {}, {},
+                             NonWriteableFields, NonReadableFields);
+
+  CollectAccessableFields(PayloadType, NonWriteableFields, NonReadableFields,
+                          WriteableFields, ReadableFields);
+
+  // Find all writes to Payload that reaches the Trace
+  DxrShaderDiagnoseInfo TraceInfo;
+  TraceInfo.Payload = Payload;
+  std::set<const CFGBlock *> Visited;
+
+  const CFGBlock *Parent = Trace.Parent;
+  Visited.insert(Parent);
+  // Collect payload accesses in the same block until we reach the TraceRay call
+  for (auto Element : *Parent) {
+    if (Optional<CFGStmt> S = Element.getAs<CFGStmt>()) {
+      if (S->getStmt() == Trace.Call)
+        break;
+      CollectReadsWritesAndCallsForPayload(S->getStmt(), TraceInfo, Parent);
+    }
+  }
+
+  for (auto I = Parent->pred_begin(); I != Parent->pred_end(); ++I) {
+    CFGBlock *Pred = *I;
+    if (!Pred)
+      continue;
+    BackwardTraverseCFGAndCollectReadsWrites(*Pred, TraceInfo, Visited);
+  }
+
+  // Warn if a writeable field has not been written.
+  for (const FieldDecl *Field : WriteableFields) {
+    if (!TraceInfo.WritesPerField.count(Field)) {
+      S.Diag(Trace.Call->getArg(7)->getExprLoc(),
+             diag::warn_hlsl_payload_access_no_write_for_trace_payload)
+          << Field->getName();
+    }
+  }
+  // Warn if a written field is not write(caller)
+  for (const FieldDecl *Field : NonWriteableFields) {
+    if (TraceInfo.WritesPerField.count(Field)) {
+      S.Diag(
+          Trace.Call->getArg(7)->getExprLoc(),
+          diag::warn_hlsl_payload_access_write_but_no_write_for_trace_payload)
+          << Field->getName();
+    }
+  }
+
+  // After a trace call, collect all reads that are not dominated by another
+  // write warn if a field is not read(caller) but the value is read (undef
+  // read).
+
+  // Discard reads/writes from backward traversal.
+  TraceInfo.ReadsPerField.clear();
+  TraceInfo.WritesPerField.clear();
+  bool CallFound = false;
+  for (auto Element : *Parent) { // TODO: reverse iterate?
+    if (Optional<CFGStmt> S = Element.getAs<CFGStmt>()) {
+      if (S->getStmt() == Trace.Call) {
+        CallFound = true;
+        continue;
+      }
+      if (CallFound)
+        CollectReadsWritesAndCallsForPayload(S->getStmt(), TraceInfo, Parent);
+    }
+  }
+  for (auto I = Parent->succ_begin(); I != Parent->succ_end(); ++I) {
+    CFGBlock *Pred = *I;
+    if (!Pred)
+      continue;
+    ForwardTraverseCFGAndCollectReadsWrites(*Pred, TraceInfo, Visited);
+  }
+
+  for (const FieldDecl *Field : ReadableFields) {
+    if (!TraceInfo.ReadsPerField.count(Field)) {
+      S.Diag(Trace.Call->getArg(7)->getExprLoc(),
+             diag::warn_hlsl_payload_access_read_but_no_read_after_trace)
+          << Field->getName();
+    }
+  }
+
+  for (const FieldDecl *Field : NonReadableFields) {
+    auto WritesToField = TraceInfo.WritesPerField.find(Field);
+    bool FieldHasWrites = WritesToField != TraceInfo.WritesPerField.end();
+    for (auto &Read : TraceInfo.ReadsPerField[Field]) {
+      bool ReadIsDominatedByWrite = false;
+      if (FieldHasWrites) {
+        // We found a read to a field that needs diagnose.
+        // We do not want to warn about fields that read but are dominated by
+        // a write. Find writes that dominate the read. If we found one,
+        // ignore the read.
+        for (auto Write : WritesToField->second) {
+          ReadIsDominatedByWrite = WriteDominatesUse(Write, Read, DT);
+          if (ReadIsDominatedByWrite)
+            break;
+        }
+      }
+
+      if (ReadIsDominatedByWrite)
+        continue;
+
+      S.Diag(Read.Member->getExprLoc(),
+             diag::warn_hlsl_payload_access_read_of_undef_after_trace)
+          << Field->getName();
+    }
+  }
+}
+
+// Emit diagnostics for all TraceRay calls.
+void DiagnoseTraceCalls(Sema &S, CFG &ShaderCFG, DominatorTree &DT,
+                        DxrShaderDiagnoseInfo &Info) {
+  // Collect TraceRay calls in the shader.
+  std::set<const CFGBlock *> Visited;
+  ForwardTraverseCFGAndCollectTraceCalls(ShaderCFG.getEntry(), Info, Visited);
+
+  std::set<const CallExpr *> Diagnosed;
+
+  for (const TraceRayCall &TraceCall : Info.TraceCalls) {
+    if (Diagnosed.count(TraceCall.Call))
+      continue;
+    Diagnosed.insert(TraceCall.Call);
+
+    const VarDecl *Payload = GetPayloadParameterForTraceCall(TraceCall.Call);
+    DiagnoseTraceCall(S, Payload, TraceCall, DT);
+  }
+}
+
+// Emit diagnostics for all access to the payload of a shader,
+// and the input to TraceRay calls.
+std::vector<const FieldDecl *>
+DiagnosePayloadAccess(Sema &S, DxrShaderDiagnoseInfo &Info,
+                      const std::set<const FieldDecl *> &FieldsToIgnoreRead,
+                      const std::set<const FieldDecl *> &FieldsToIgnoreWrite,
+                      std::set<const FunctionDecl *> VisitedFunctions) {
+  clang::DominatorTree DT;
+  AnalysisDeclContextManager AnalysisManager;
+  AnalysisDeclContext *AnalysisContext =
+      AnalysisManager.getContext(Info.FunctionDecl);
+
+  CFG &TheCFG = *AnalysisContext->getCFG();
+  DT.buildDominatorTree(*AnalysisContext);
+
+  // Collect all Fields that gets written to return it back up through the
+  // recursion.
+  std::vector<const FieldDecl *> WrittenFields;
+
+  // Skip if we are in a RayGeneration shader without payload.
+  if (Info.Payload) {
+    std::vector<FieldDecl *> NonWriteableFields;
+    std::vector<FieldDecl *> NonReadableFields;
+    RecordDecl *PayloadType = GetPayloadType(Info.Payload);
+    if (!PayloadType)
+      return WrittenFields;
+
+    CollectNonAccessableFields(PayloadType, Info.Stage, FieldsToIgnoreRead,
+                               FieldsToIgnoreWrite, NonWriteableFields,
+                               NonReadableFields);
+
+    std::set<const CFGBlock *> Visited;
+    ForwardTraverseCFGAndCollectReadsWrites(TheCFG.getEntry(), Info, Visited);
+
+    if (Info.Payload->hasAttr<HLSLOutAttr>() ||
+        Info.Payload->hasAttr<HLSLInOutAttr>()) {
+      // If there is copy-out semantic on the payload field,
+      // save the written fields and return it back to the caller for
+      // better diagnostics in higher recursion levels.
+
+      for (auto &p : Info.WritesPerField) {
+        WrittenFields.push_back(p.first);
+      }
+
+      DiagnosePayloadWrites(S, TheCFG, DT, Info, NonWriteableFields,
+                            PayloadType);
+    }
+
+    auto WrittenFieldsInCalls = DiagnosePayloadAsFunctionArg(
+        S, Info, DT, FieldsToIgnoreRead, FieldsToIgnoreWrite, VisitedFunctions);
+
+    // Add calls that write fields as writes to allow the diagnostics on reads
+    // to check if a call that writes the field dominates the read.
+
+    for (auto& P : WrittenFieldsInCalls) {
+        for (const FieldDecl* Field : P.second) {
+            Info.WritesPerField[Field].push_back(P.first);
+      }
+    }
+
+    if (Info.Payload->hasAttr<HLSLInAttr>() ||
+        Info.Payload->hasAttr<HLSLInOutAttr>())
+      DiagnosePayloadReads(S, TheCFG, DT, Info, NonReadableFields);
+  }
+
+  DiagnoseTraceCalls(S, TheCFG, DT, Info);
+
+  return WrittenFields;
+}
+
+const Stmt *IgnoreParensAndDecay(const Stmt *S) {
+  for (;;) {
+    switch (S->getStmtClass()) {
+    case Stmt::ParenExprClass:
+      S = cast<ParenExpr>(S)->getSubExpr();
+      break;
+    case Stmt::ImplicitCastExprClass: {
+      const ImplicitCastExpr *castExpr = cast<ImplicitCastExpr>(S);
+      if (castExpr->getCastKind() != CK_ArrayToPointerDecay &&
+          castExpr->getCastKind() != CK_NoOp &&
+          castExpr->getCastKind() != CK_LValueToRValue) {
+        return S;
+      }
+      S = castExpr->getSubExpr();
+    } break;
+    default:
+      return S;
+    }
+  }
+}
+
+// Emit warnings for calls that pass the payload to extern functions.
+bool DiagnoseCallExprForExternal(Sema &S, const FunctionDecl *FD,
+                                 const CallExpr *CE,
+                                 const ParmVarDecl *Payload) {
+  // We check if we are passing the entire payload struct to an extern function.
+  // Here ends what we can check, so we just issue a warning.
+  if (!FD->hasBody()) {
+    const DeclRefExpr *DRef = nullptr;
+    const ParmVarDecl *PDecl = nullptr;
+    for (unsigned i = 0; i < CE->getNumArgs(); ++i) {
+      const Stmt *arg = IgnoreParensAndDecay(CE->getArg(i));
+      if (const DeclRefExpr *ArgRef = dyn_cast<DeclRefExpr>(arg)) {
+        if (ArgRef->getDecl() == Payload) {
+          DRef = ArgRef;
+          PDecl = FD->getParamDecl(i);
+          break;
+        }
+      }
+    }
+
+    if (DRef) {
+      S.Diag(CE->getExprLoc(),
+             diag::warn_qualified_payload_passed_to_extern_function);
+      return true;
+    }
+  }
+  return false;
+}
+
+// Emits diagnostics for the Payload parameter of a DXR shader stage.
+bool DiagnosePayloadParameter(Sema &S, ParmVarDecl *Payload, FunctionDecl *FD,
+                              DXIL::PayloadAccessShaderStage stage) {
+  if (!Payload) {
+    // cought already during codgegen of the function
+    return false;
+  }
+  if (!Payload->getAttr<HLSLInOutAttr>()) {
+    // error: payload must be inout qualified
+    return false;
+  }
+
+  CXXRecordDecl *Decl = Payload->getType()->getAsCXXRecordDecl();
+  if (!Decl || Decl->isImplicit()) {
+    // error: not a user defined type decl
+    return false;
+  }
+
+  if (!Decl->hasAttr<HLSLRayPayloadAttr>()) {
+    S.Diag(Payload->getLocation(), diag::err_payload_requires_attribute)
+        << Decl->getName();
+    return false;
+  }
+
+  return true;
+}
+
+class DXRShaderVisitor : public RecursiveASTVisitor<DXRShaderVisitor> {
+public:
+  DXRShaderVisitor(Sema &S) : S(S) {}
+
+  void diagnose(TranslationUnitDecl *TU) { TraverseTranslationUnitDecl(TU); }
+
+  bool VisitFunctionDecl(FunctionDecl *Decl) {
+    auto attr = Decl->getAttr<HLSLShaderAttr>();
+    if (!attr)
+      return true;
+
+    StringRef shaderStage = attr->getStage();
+    if (StringRef("miss,closesthit,anyhit,raygeneration").count(shaderStage)) {
+      ParmVarDecl *Payload = nullptr;
+      if (shaderStage != "raygeneration")
+        Payload = Decl->getParamDecl(0);
+
+      DXIL::PayloadAccessShaderStage Stage =
+          DXIL::PayloadAccessShaderStage::Invalid;
+      if (shaderStage == "closesthit") {
+        Stage = DXIL::PayloadAccessShaderStage::Closesthit;
+      } else if (shaderStage == "miss") {
+        Stage = DXIL::PayloadAccessShaderStage::Miss;
+      } else if (shaderStage == "anyhit") {
+        Stage = DXIL::PayloadAccessShaderStage::Anyhit;
+      }
+      // Diagnose the payload parameter.
+      if (Payload) {
+        DiagnosePayloadParameter(S, Payload, Decl, Stage);
+      }
+      DxrShaderDiagnoseInfo Info;
+      Info.FunctionDecl = Decl;
+      Info.Payload = Payload;
+      Info.Stage = Stage;
+
+      std::set<const FunctionDecl *> VisitedFunctions;
+      DiagnosePayloadAccess(S, Info, {}, {}, VisitedFunctions);
+    }
+    return true;
+  }
+
+private:
+  Sema &S;
+};
+} // namespace
+
+namespace hlsl {
+
+void DiagnoseRaytracingPayloadAccess(clang::Sema &S,
+                                     clang::TranslationUnitDecl *TU) {
+  DXRShaderVisitor visitor(S);
+  visitor.diagnose(TU);
+}
+
+} // namespace hlsl

+ 217 - 0
tools/clang/lib/Sema/SemaHLSL.cpp

@@ -2961,6 +2961,8 @@ namespace hlsl {
       return CheckRecursion(CallStack, EntryFnDecl);
     }
 
+    const CallNodes &GetCallGraph() { return m_callNodes; }
+
     void dump() const {
       OutputDebugStringW(L"Call Nodes:\r\n");
       for (auto &node : m_callNodes) {
@@ -10236,6 +10238,20 @@ void hlsl::DiagnoseTranslationUnit(clang::Sema *self) {
   if (self->getDiagnostics().hasErrorOccurred()) {
     return;
   }
+
+  // Check RT shader if available for their payload use and match payload access 
+  // against availiable payload modifiers. 
+  // We have to do it late because we could have payload access in a called function
+  // and have to check the callgraph if the root shader has the right access
+  // rights to the payload structure. 
+  if (self->getLangOpts().IsHLSLLibrary) {
+    if (self->getLangOpts().EnablePayloadAccessQualifiers) {
+      ASTContext &ctx = self->getASTContext();
+      TranslationUnitDecl *TU = ctx.getTranslationUnitDecl();
+      DiagnoseRaytracingPayloadAccess(*self, TU);
+    }
+  }
+
   // Don't check entry function for library.
   if (self->getLangOpts().IsHLSLLibrary) {
     // TODO: validate no recursion start from every function.
@@ -10348,6 +10364,184 @@ void hlsl::DiagnoseTranslationUnit(clang::Sema *self) {
   }
 }
 
+void hlsl::DiagnosePayloadAccessQualifierAnnotations(
+    Sema &S, Declarator& D, const QualType& T, const std::vector<hlsl::UnusualAnnotation *>& annotations) {
+
+  auto &&iter = annotations.begin();
+  auto &&end = annotations.end();
+
+  hlsl::PayloadAccessAnnotation *readAnnotation = nullptr;
+  hlsl::PayloadAccessAnnotation *writeAnnotation = nullptr;
+
+  for (; iter != end; ++iter) {
+    switch ((*iter)->getKind()) {
+    case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier: {
+      hlsl::PayloadAccessAnnotation *annotation =
+          cast<hlsl::PayloadAccessAnnotation>(*iter);
+      if (annotation->qualifier == DXIL::PayloadAccessQualifier::Read) {
+        if (!readAnnotation)
+          readAnnotation = annotation;
+        else {
+          S.Diag(annotation->Loc,
+                 diag::err_hlsl_payload_access_qualifier_multiple_defined)
+              << "read";
+          return;
+        }
+      } else if (annotation->qualifier == DXIL::PayloadAccessQualifier::Write) {
+        if (!writeAnnotation)
+          writeAnnotation = annotation;
+        else {
+          S.Diag(annotation->Loc,
+                 diag::err_hlsl_payload_access_qualifier_multiple_defined)
+              << "write";
+          return;
+        }
+      }
+
+      break;
+    }
+    default:
+      // Ignore all other annotations here.
+      break;
+    }
+  }
+
+  struct PayloadAccessQualifierInformation{
+    bool anyhit = false;
+    bool closesthit = false;
+    bool miss = false;
+    bool caller = false;
+  } readQualContains, writeQualContains;
+
+  auto collectInformationAboutShaderStages =
+      [&](hlsl::PayloadAccessAnnotation *annotation,
+          PayloadAccessQualifierInformation &info) {
+        for (auto shaderType : annotation->ShaderStages) {
+          if (shaderType == DXIL::PayloadAccessShaderStage::Anyhit)
+            info.anyhit = true;
+          else if (shaderType == DXIL::PayloadAccessShaderStage::Closesthit)
+            info.closesthit = true;
+          else if (shaderType == DXIL::PayloadAccessShaderStage::Miss)
+            info.miss = true;
+          else if (shaderType == DXIL::PayloadAccessShaderStage::Caller)
+            info.caller = true;
+        }
+        return true;
+      };
+
+  if (readAnnotation) {
+    if (!collectInformationAboutShaderStages(readAnnotation, readQualContains))
+      return;
+  }
+  if (writeAnnotation) {
+    if (!collectInformationAboutShaderStages(writeAnnotation, writeQualContains))
+      return;
+  }
+
+  if (writeAnnotation) {
+    // Note: keep the following two checks separated to diagnose both
+    //       stages (closesthit and miss)
+    // If closesthit/miss writes a value the caller must consume it.
+    if (writeQualContains.miss) {
+      if (!readAnnotation || !readQualContains.caller) {
+         S.Diag(writeAnnotation->Loc,
+               diag::err_hlsl_payload_access_qualifier_invalid_combination)
+            << D.getIdentifier()
+            << "write"
+            << "miss"
+            << "consumer";
+      }
+    }    
+    if (writeQualContains.closesthit) {
+      if (!readAnnotation || !readQualContains.caller) {
+         S.Diag(writeAnnotation->Loc,
+               diag::err_hlsl_payload_access_qualifier_invalid_combination)
+            << D.getIdentifier()
+            << "write"
+            << "closesthit"
+            << "consumer";
+      }
+    }
+
+    // If anyhit writes, we need at least one consumer
+    if (writeQualContains.anyhit && !readAnnotation) {
+         S.Diag(writeAnnotation->Loc,
+               diag::err_hlsl_payload_access_qualifier_invalid_combination)
+            << D.getIdentifier()
+            << "write"
+            << "anyhit"
+            << "consumer";
+    }
+
+    // If the caller writes, we need at least one consumer
+    if (writeQualContains.caller && !readAnnotation) {
+         S.Diag(writeAnnotation->Loc,
+               diag::err_hlsl_payload_access_qualifier_invalid_combination)
+            << D.getIdentifier()
+            << "write"
+            << "caller"
+            << "consumer";
+    }
+  }
+
+  // Validate the read qualifer if present.
+  if (readAnnotation) {
+    // Note: keep the following two checks separated to diagnose both
+    //       stages (closesthit and miss)
+    // If closeshit/miss consume a value we need a producer.
+    // Valid producers are the caller and anyhit.
+    if (readQualContains.miss) {
+      if (!writeAnnotation ||
+          !(writeQualContains.anyhit || writeQualContains.caller)) {
+         S.Diag(readAnnotation->Loc,
+               diag::err_hlsl_payload_access_qualifier_invalid_combination)
+            << D.getIdentifier()
+            << "read"
+            << "miss"
+            << "producer";
+      }
+    }
+
+    // If closeshit/miss consume a value we need a producer.
+    // Valid producers are the caller and anyhit.
+    if (readQualContains.closesthit) {
+      if (!writeAnnotation ||
+          !(writeQualContains.anyhit || writeQualContains.caller)) {
+         S.Diag(readAnnotation->Loc,
+               diag::err_hlsl_payload_access_qualifier_invalid_combination)
+            << D.getIdentifier()
+            << "read"
+            << "closesthit"
+            << "producer";
+      }
+    }    
+
+    // If anyhit consumes the value we need a producer.
+    // Valid producers are the caller and antoher anyhit.
+    if (readQualContains.anyhit) {
+      if (!writeAnnotation ||
+          !(writeQualContains.anyhit || writeQualContains.caller)) {
+        S.Diag(readAnnotation->Loc,
+               diag::err_hlsl_payload_access_qualifier_invalid_combination)
+            << D.getIdentifier()
+            << "read"
+            << "anyhit"
+            << "producer";
+      }
+    }
+
+    // If the caller consumes the value we need a valid producer.
+    if (readQualContains.caller && !writeAnnotation) {
+         S.Diag(readAnnotation->Loc,
+               diag::err_hlsl_payload_access_qualifier_invalid_combination)
+            << D.getIdentifier()
+            << "read"
+            << "caller"
+            << "producer";
+    }
+  }
+}
+
 void hlsl::DiagnoseUnusualAnnotationsForHLSL(
   Sema& S,
   std::vector<hlsl::UnusualAnnotation *>& annotations)
@@ -10406,6 +10600,10 @@ void hlsl::DiagnoseUnusualAnnotationsForHLSL(
       // No common validation to be performed.
       break;
     }
+    case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier: {
+      // Validation happens sperately
+      break;
+    }
     }
   }
 }
@@ -11547,6 +11745,10 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
     break;
   case AttributeList::AT_HLSLPayload:
     declAttr = ::new (S.Context) HLSLPayloadAttr(
+        A.getRange(), S.Context, A.getAttributeSpellingListIndex());  
+    break;
+  case AttributeList::AT_HLSLRayPayload:
+    declAttr = ::new (S.Context) HLSLRayPayloadAttr(
         A.getRange(), S.Context, A.getAttributeSpellingListIndex());
     break;
 
@@ -11867,6 +12069,11 @@ Decl* Sema::ActOnStartHLSLBuffer(
       // Ignore semantic declarations.
       break;
     }
+    case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier: {
+      hlsl::PayloadAccessAnnotation* annotation = cast<hlsl::PayloadAccessAnnotation>(*unusualIter);
+      Diag( annotation->Loc, diag::err_hlsl_unsupported_payload_access_qualifier);
+      break;
+    }
     }
   }
 
@@ -12533,6 +12740,9 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth,
 
   // Validate unusual annotations.
   hlsl::DiagnoseUnusualAnnotationsForHLSL(*this, D.UnusualAnnotations);
+  if (isField)
+    hlsl::DiagnosePayloadAccessQualifierAnnotations(*this, D, qt,
+                                                    D.UnusualAnnotations);
   auto && unusualIter = D.UnusualAnnotations.begin();
   auto && unusualEnd = D.UnusualAnnotations.end();
   for (; unusualIter != unusualEnd; ++unusualIter) {
@@ -12570,6 +12780,13 @@ bool Sema::DiagnoseHLSLDecl(Declarator &D, DeclContext *DC, Expr *BitWidth,
         Diag(semanticDecl->Loc, diag::err_hlsl_varmodifierna)
             << "semantic" << declarationType;
       }
+      break;    
+    }
+    case hlsl::UnusualAnnotation::UA_PayloadAccessQualifier: {
+      hlsl::PayloadAccessAnnotation *annotation = cast<hlsl::PayloadAccessAnnotation>(*unusualIter);
+      if (!isField) {
+          Diag(annotation->Loc, diag::err_hlsl_unsupported_payload_access_qualifier);
+      }
       break;
     }
     }

+ 232 - 0
tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/access.hlsl

@@ -0,0 +1,232 @@
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=0 | FileCheck -check-prefix=CHK0 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=1 | FileCheck -check-prefix=CHK1 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=2 | FileCheck -check-prefix=CHK2 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=3 | FileCheck -check-prefix=CHK3 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=4 | FileCheck -check-prefix=CHK4 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=5 | FileCheck -check-prefix=CHK5 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=6 | FileCheck -check-prefix=CHK6 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=7 | FileCheck -check-prefix=CHK7 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=8 | FileCheck -check-prefix=CHK8 -input=stderr  %s
+
+// CHK0-NOT: -Wpayload-access-
+
+// CHK1: warning: write will be dropped ('noWrite' is not qualified 'write' for shader stage 'closesthit')
+// CHK1: warning: reading undefined value ('noRead' is not qualified 'read' for shader stage 'closesthit')
+
+// CHK2: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+// CHK2-NOT: warning: reading undefined value ('noRead' is not qualified 'read' for shader stage 'closesthit')
+
+// CHK3: warning: write will be dropped ('noWrite' is not qualified 'write' for shader stage 'closesthit')
+// CHK3: warning: reading undefined value ('noRead' is not qualified 'read' for shader stage 'closesthit')
+// CHK3: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+// CHK3: warning: write will be dropped ('noWrite3' is not qualified 'write' for shader stage 'closesthit')
+// CHK3-NOT: warning: write will be dropped ('noWrite2' is not qualified 'write' for shader stage 'closesthit')
+// CHK3-NOT: warning: reading undefined value ('noRead3' is not qualified 'read' for shader stage 'closesthit')
+// CHK3-NOT: fooload
+
+// CHK4: warning: write will be dropped ('noWrite' is not qualified 'write' for shader stage 'closesthit')
+// CHK4: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+// CHK4: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+
+// CHK5: warning: field 'noWrite' is 'write' for 'caller' stage but field is never written for TraceRay call
+// CHK5: warning: field 'noWrite2' is 'write' for 'caller' stage but field is never written for TraceRay call
+// CHK5: warning: field 'noWrite3' is 'write' for 'caller' stage but field is never written for TraceRay call
+// CHK5: warning: value will be undefined inside TraceRay ('noRead' is not qualified 'write' for 'caller')
+// CHK5: warning: 'noRead' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)
+// CHK5: warning: 'noRead2' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)
+// CHK5: warning: 'noRead3' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)
+// CHK5: warning: 'readWrite' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)
+// CHK5: warning: reading undefined value ('noWrite' is returned from TraceRay but not qualified 'read' for 'caller')
+
+// CHK6: warning: potential loss of data for payload field 'clobbered'. Field is qualified 'write' in earlier stages and 'write' only for stage 'closesthit' but never unconditionally written.
+
+// CHK7: warning: potential loss of data for payload field 'clobbered'. Field is qualified 'write' in earlier stages and 'write' only for stage 'anyhit' but never unconditionally written.
+
+// CHK8: warning: write will be dropped ('noWrite' is not qualified 'write' for shader stage 'closesthit')
+// CHK8: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+// CHK8-NOT: warning: reading undefined value ('noRead3' is not qualified 'read' for shader stage 'closesthit')
+
+struct [raypayload] Payload
+{
+    float noRead : write(closesthit) : read(caller); 
+    float noRead2 : write(closesthit) : read(caller); 
+    float noRead3 : write(closesthit) : read(caller); 
+    float noWrite  : read(closesthit) : write(caller); 
+    float noWrite2  : read(closesthit) : write(caller); 
+    float noWrite3  : read(closesthit) : write(caller); 
+    float readWrite : write(closesthit,  caller) : read(caller, closesthit); 
+#if TEST_NUM == 6
+    float clobbered : write(caller, closesthit) : read(caller);
+#endif
+#if TEST_NUM == 7
+    float clobbered : write(anyhit, closesthit) : read(caller);
+#endif
+};
+
+struct Attribs { float2 barys; };
+
+
+// Check if no warning is produced if no access happens.
+#if TEST_NUM == 0
+[shader("closesthit")]
+void ClosestHit0( inout Payload payload, in Attribs attribs )
+{
+}
+#endif
+
+// Check if we produce a warning: noRead is undefined
+//                                noWrite is lost
+#if TEST_NUM == 1
+[shader("closesthit")]
+void ClosestHit1( inout Payload payload, in Attribs attribs )
+{
+    payload.noWrite = payload.noRead;
+}
+#endif
+
+// Check if we do not produce warnings for fields dominated by a write.
+// noRead is dominated by a write and thus has a vaild value.
+// noRead2 is not written and undefied because of access qualifiers.
+#if TEST_NUM == 2
+[shader("closesthit")]
+void ClosestHit3( inout Payload payload, in Attribs attribs )
+{
+    payload.noRead = 24;
+    if (payload.noRead)
+        int x = 2 + payload.noRead + payload.noRead2; // warning, read is not dominated by write
+}
+#endif
+
+// Check if we warn in a function that gets the payload as parameter.
+// This should produce warnings on the access to payload but not to fooload.
+// For foo we expect a warning for the read and the write.
+// For foo_in we expect only a warning for the read since the payload is not copy-out.
+// For foo_out we expect only a warning for the write since the payload is not copy-in.
+#if TEST_NUM == 3
+void foo(inout Payload fooload, inout Payload payload)
+{
+    if (fooload.noRead)
+        payload.noWrite = payload.noRead;
+}
+void foo_in(inout Payload fooload, in Payload payload)
+{
+    if (fooload.noRead)
+        payload.noWrite2 = payload.noRead2;
+}
+
+void foo_out(inout Payload fooload, out Payload payload)
+{
+    if (fooload.noRead)
+        payload.noWrite3 = payload.noRead3;
+}
+
+[shader("closesthit")]
+void ClosestHit4( inout Payload payload, in Attribs attribs )
+{
+    Payload fooload;
+    if (payload.readWrite)
+        foo(fooload, payload);
+}
+
+[shader("closesthit")]
+void ClosestHit5( inout Payload payload, in Attribs attribs )
+{
+    Payload fooload;
+    if (payload.readWrite)
+        foo_in(fooload, payload);
+}
+
+[shader("closesthit")]
+void ClosestHit6( inout Payload payload, in Attribs attribs )
+{
+    Payload fooload;
+    if (payload.readWrite)
+        foo_out(fooload, payload);
+}
+#endif
+
+// Check if we don't crash if we have to handle loops and recursion.
+#if TEST_NUM == 4
+void bar(inout Payload payload)
+{
+    payload.noWrite = payload.noRead;
+    bar(payload);
+    payload.noWrite = payload.noRead2;
+}
+
+
+[shader("closesthit")]
+void ClosestHit8( inout Payload payload, in Attribs attribs )
+{
+    payload.noRead = 1;
+    bar(payload);
+    for (int i = 0; i < payload.noRead2; ++i)
+    payload.noWrite = 2;
+}
+#endif
+
+// Test if we produce warnings for TraceRay calls. 
+// In the following the noWrite fields which are write for 'caller' are not written (warn).
+// The noRead field is written to in the caller but is not qualified 'write' warn about lost input.
+// Several fields are marked as 'read' for 'caller' but never read. Warn about potential perf issue.
+// The noWrite field is used after the trace call but the field is not qualified 'read' for 'caller', 
+// the value will be dropped and the read value is undefined (warn).
+#if TEST_NUM == 5
+RaytracingAccelerationStructure scene : register(t0);
+
+[shader("raygeneration")]
+void RayGen1()
+{
+    Payload payload;
+    payload.readWrite = 0;
+    payload.noRead = 0;
+    RayDesc ray;
+    TraceRay( scene, RAY_FLAG_NONE, 0xff, 0, 1, 0, ray, payload );
+
+    if (payload.noWrite)
+    {
+        payload.noWrite = 23;
+    }
+    payload.noWrite = 32;
+}
+#endif
+
+// Check if we produce a warning for a shader that has write access but does not write and 
+// clobbers a field written by an earlier shader stage.
+#if TEST_NUM == 6
+[shader("closesthit")]
+void ClosestHit0( inout Payload payload, in Attribs attribs )
+{
+}
+#endif
+
+// Check if we produce a warning for a shader that has write access but does not write and 
+// clobbers a field written by an earlier shader stage. Here we consider anyhit an earlier stage
+// for anyhit.
+#if TEST_NUM == 7
+[shader("anyhit")]
+void Anyhit0( inout Payload payload, in Attribs attribs  )
+{
+}
+#endif
+
+// Check if a write in a function slience the warning about an undef read in the caller.
+#if TEST_NUM == 8
+void bar(inout Payload payload)
+{
+    payload.noWrite = payload.noRead;
+    bar(payload);
+    payload.noWrite = payload.noRead2;
+    payload.noRead3 = 5;
+}
+
+
+[shader("closesthit")]
+void ClosestHit8( inout Payload payload, in Attribs attribs )
+{
+    payload.noRead = 1;
+    bar(payload);
+    for (int i = 0; i < payload.noRead3; ++i)
+    payload.noWrite = 2;
+}
+#endif

+ 508 - 0
tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/combination.hlsl

@@ -0,0 +1,508 @@
+// RUN: %dxc -T lib_6_6 %s -enable-payload-qualifiers | FileCheck %s
+
+// CHECK: error: field 'x1' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x2' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x3' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x3' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x4' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x5' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x5' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x6' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x6' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x7' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x7' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x7' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x8' is qualified 'read' for shader stage 'caller' but has no valid producer
+// CHECK: error: field 'x9' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x9' is qualified 'read' for shader stage 'caller' but has no valid producer
+// CHECK: error: field 'x10' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x10' is qualified 'read' for shader stage 'caller' but has no valid producer
+// CHECK: error: field 'x11' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x11' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x11' is qualified 'read' for shader stage 'caller' but has no valid producer
+// CHECK: error: field 'x12' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x12' is qualified 'read' for shader stage 'caller' but has no valid producer
+// CHECK: error: field 'x13' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x13' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x13' is qualified 'read' for shader stage 'caller' but has no valid producer
+// CHECK: error: field 'x14' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x14' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x14' is qualified 'read' for shader stage 'caller' but has no valid producer
+// CHECK: error: field 'x15' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x15' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x15' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x15' is qualified 'read' for shader stage 'caller' but has no valid producer
+// CHECK: error: field 'x16' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x17' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x17' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x18' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x18' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x19' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x19' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x19' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x20' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x20' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x21' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x21' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x21' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x22' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x22' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x22' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x23' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x23' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x23' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x23' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x25' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x26' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x27' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x27' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x28' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x29' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x29' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x30' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x30' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x31' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x31' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x31' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x32' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x33' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x33' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x34' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x34' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x35' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x35' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x35' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x36' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x36' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x37' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x37' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x37' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x38' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x38' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x38' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x39' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x39' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x39' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x39' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x41' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x42' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x43' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x43' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x44' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x45' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x45' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x46' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x46' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x47' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x47' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x47' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x48' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x48' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x49' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x49' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x49' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x50' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x50' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x50' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x51' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x51' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x51' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x51' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x52' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x52' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x52' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x53' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x53' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x53' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x53' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x54' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x54' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x54' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x54' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x55' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x55' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x55' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x55' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x55' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x57' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x58' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x59' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x59' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x60' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x61' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x61' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x62' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x62' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x63' is qualified 'read' for shader stage 'miss' but has no valid producer
+// CHECK: error: field 'x63' is qualified 'read' for shader stage 'closesthit' but has no valid producer
+// CHECK: error: field 'x63' is qualified 'read' for shader stage 'anyhit' but has no valid producer
+// CHECK: error: field 'x64' is qualified 'write' for shader stage 'anyhit' but has no valid consumer
+// CHECK: error: field 'x80' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x80' is qualified 'write' for shader stage 'anyhit' but has no valid consumer
+// CHECK: error: field 'x81' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x82' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x83' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x84' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x85' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x86' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x87' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x96' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x96' is qualified 'write' for shader stage 'anyhit' but has no valid consumer
+// CHECK: error: field 'x97' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x98' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x99' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x100' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x101' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x102' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x103' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x112' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x112' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x112' is qualified 'write' for shader stage 'anyhit' but has no valid consumer
+// CHECK: error: field 'x113' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x113' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x114' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x114' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x115' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x115' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x116' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x116' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x117' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x117' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x118' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x118' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x119' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x119' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x128' is qualified 'write' for shader stage 'caller' but has no valid consumer
+// CHECK: error: field 'x144' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x144' is qualified 'write' for shader stage 'caller' but has no valid consumer
+// CHECK: error: field 'x145' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x146' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x147' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x148' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x149' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x150' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x151' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x160' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x160' is qualified 'write' for shader stage 'caller' but has no valid consumer
+// CHECK: error: field 'x161' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x162' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x163' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x164' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x165' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x166' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x167' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x176' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x176' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x176' is qualified 'write' for shader stage 'caller' but has no valid consumer
+// CHECK: error: field 'x177' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x177' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x178' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x178' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x179' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x179' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x180' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x180' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x181' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x181' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x182' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x182' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x183' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x183' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x192' is qualified 'write' for shader stage 'anyhit' but has no valid consumer
+// CHECK: error: field 'x192' is qualified 'write' for shader stage 'caller' but has no valid consumer
+// CHECK: error: field 'x208' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x208' is qualified 'write' for shader stage 'anyhit' but has no valid consumer
+// CHECK: error: field 'x208' is qualified 'write' for shader stage 'caller' but has no valid consumer
+// CHECK: error: field 'x209' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x210' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x211' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x212' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x213' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x214' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x215' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x224' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x224' is qualified 'write' for shader stage 'anyhit' but has no valid consumer
+// CHECK: error: field 'x224' is qualified 'write' for shader stage 'caller' but has no valid consumer
+// CHECK: error: field 'x225' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x226' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x227' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x228' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x229' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x230' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x231' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x240' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x240' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x240' is qualified 'write' for shader stage 'anyhit' but has no valid consumer
+// CHECK: error: field 'x240' is qualified 'write' for shader stage 'caller' but has no valid consumer
+// CHECK: error: field 'x241' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x241' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x242' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x242' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x243' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x243' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x244' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x244' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x245' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x245' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x246' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x246' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+// CHECK: error: field 'x247' is qualified 'write' for shader stage 'miss' but has no valid consumer
+// CHECK: error: field 'x247' is qualified 'write' for shader stage 'closesthit' but has no valid consumer
+struct [payload] MyPayload {
+int x0 : read();
+int x1 : read(miss);
+int x2 : read(closesthit);
+int x3 : read(miss, closesthit);
+int x4 : read(anyhit);
+int x5 : read(miss, anyhit);
+int x6 : read(closesthit, anyhit);
+int x7 : read(miss, closesthit, anyhit);
+int x8 : read(caller);
+int x9 : read(miss, caller);
+int x10 : read(closesthit, caller);
+int x11 : read(miss, closesthit, caller);
+int x12 : read(anyhit, caller);
+int x13 : read(miss, anyhit, caller);
+int x14 : read(closesthit, anyhit, caller);
+int x15 : read(miss, closesthit, anyhit, caller);
+int x16 : write(miss);
+int x17 : write(miss) : read(miss);
+int x18 : write(miss) : read(closesthit);
+int x19 : write(miss) : read(miss, closesthit);
+int x20 : write(miss) : read(anyhit);
+int x21 : write(miss) : read(miss, anyhit);
+int x22 : write(miss) : read(closesthit, anyhit);
+int x23 : write(miss) : read(miss, closesthit, anyhit);
+int x24 : write(miss) : read(caller);
+int x25 : write(miss) : read(miss, caller);
+int x26 : write(miss) : read(closesthit, caller);
+int x27 : write(miss) : read(miss, closesthit, caller);
+int x28 : write(miss) : read(anyhit, caller);
+int x29 : write(miss) : read(miss, anyhit, caller);
+int x30 : write(miss) : read(closesthit, anyhit, caller);
+int x31 : write(miss) : read(miss, closesthit, anyhit, caller);
+int x32 : write(closesthit);
+int x33 : write(closesthit) : read(miss);
+int x34 : write(closesthit) : read(closesthit);
+int x35 : write(closesthit) : read(miss, closesthit);
+int x36 : write(closesthit) : read(anyhit);
+int x37 : write(closesthit) : read(miss, anyhit);
+int x38 : write(closesthit) : read(closesthit, anyhit);
+int x39 : write(closesthit) : read(miss, closesthit, anyhit);
+int x40 : write(closesthit) : read(caller);
+int x41 : write(closesthit) : read(miss, caller);
+int x42 : write(closesthit) : read(closesthit, caller);
+int x43 : write(closesthit) : read(miss, closesthit, caller);
+int x44 : write(closesthit) : read(anyhit, caller);
+int x45 : write(closesthit) : read(miss, anyhit, caller);
+int x46 : write(closesthit) : read(closesthit, anyhit, caller);
+int x47 : write(closesthit) : read(miss, closesthit, anyhit, caller);
+int x48 : write(miss, closesthit);
+int x49 : write(miss, closesthit) : read(miss);
+int x50 : write(miss, closesthit) : read(closesthit);
+int x51 : write(miss, closesthit) : read(miss, closesthit);
+int x52 : write(miss, closesthit) : read(anyhit);
+int x53 : write(miss, closesthit) : read(miss, anyhit);
+int x54 : write(miss, closesthit) : read(closesthit, anyhit);
+int x55 : write(miss, closesthit) : read(miss, closesthit, anyhit);
+int x56 : write(miss, closesthit) : read(caller);
+int x57 : write(miss, closesthit) : read(miss, caller);
+int x58 : write(miss, closesthit) : read(closesthit, caller);
+int x59 : write(miss, closesthit) : read(miss, closesthit, caller);
+int x60 : write(miss, closesthit) : read(anyhit, caller);
+int x61 : write(miss, closesthit) : read(miss, anyhit, caller);
+int x62 : write(miss, closesthit) : read(closesthit, anyhit, caller);
+int x63 : write(miss, closesthit) : read(miss, closesthit, anyhit, caller);
+int x64 : write(anyhit);
+int x65 : write(anyhit) : read(miss);
+int x66 : write(anyhit) : read(closesthit);
+int x67 : write(anyhit) : read(miss, closesthit);
+int x68 : write(anyhit) : read(anyhit);
+int x69 : write(anyhit) : read(miss, anyhit);
+int x70 : write(anyhit) : read(closesthit, anyhit);
+int x71 : write(anyhit) : read(miss, closesthit, anyhit);
+int x72 : write(anyhit) : read(caller);
+int x73 : write(anyhit) : read(miss, caller);
+int x74 : write(anyhit) : read(closesthit, caller);
+int x75 : write(anyhit) : read(miss, closesthit, caller);
+int x76 : write(anyhit) : read(anyhit, caller);
+int x77 : write(anyhit) : read(miss, anyhit, caller);
+int x78 : write(anyhit) : read(closesthit, anyhit, caller);
+int x79 : write(anyhit) : read(miss, closesthit, anyhit, caller);
+int x80 : write(miss, anyhit);
+int x81 : write(miss, anyhit) : read(miss);
+int x82 : write(miss, anyhit) : read(closesthit);
+int x83 : write(miss, anyhit) : read(miss, closesthit);
+int x84 : write(miss, anyhit) : read(anyhit);
+int x85 : write(miss, anyhit) : read(miss, anyhit);
+int x86 : write(miss, anyhit) : read(closesthit, anyhit);
+int x87 : write(miss, anyhit) : read(miss, closesthit, anyhit);
+int x88 : write(miss, anyhit) : read(caller);
+int x89 : write(miss, anyhit) : read(miss, caller);
+int x90 : write(miss, anyhit) : read(closesthit, caller);
+int x91 : write(miss, anyhit) : read(miss, closesthit, caller);
+int x92 : write(miss, anyhit) : read(anyhit, caller);
+int x93 : write(miss, anyhit) : read(miss, anyhit, caller);
+int x94 : write(miss, anyhit) : read(closesthit, anyhit, caller);
+int x95 : write(miss, anyhit) : read(miss, closesthit, anyhit, caller);
+int x96 : write(closesthit, anyhit);
+int x97 : write(closesthit, anyhit) : read(miss);
+int x98 : write(closesthit, anyhit) : read(closesthit);
+int x99 : write(closesthit, anyhit) : read(miss, closesthit);
+int x100 : write(closesthit, anyhit) : read(anyhit);
+int x101 : write(closesthit, anyhit) : read(miss, anyhit);
+int x102 : write(closesthit, anyhit) : read(closesthit, anyhit);
+int x103 : write(closesthit, anyhit) : read(miss, closesthit, anyhit);
+int x104 : write(closesthit, anyhit) : read(caller);
+int x105 : write(closesthit, anyhit) : read(miss, caller);
+int x106 : write(closesthit, anyhit) : read(closesthit, caller);
+int x107 : write(closesthit, anyhit) : read(miss, closesthit, caller);
+int x108 : write(closesthit, anyhit) : read(anyhit, caller);
+int x109 : write(closesthit, anyhit) : read(miss, anyhit, caller);
+int x110 : write(closesthit, anyhit) : read(closesthit, anyhit, caller);
+int x111 : write(closesthit, anyhit) : read(miss, closesthit, anyhit, caller);
+int x112 : write(miss, closesthit, anyhit);
+int x113 : write(miss, closesthit, anyhit) : read(miss);
+int x114 : write(miss, closesthit, anyhit) : read(closesthit);
+int x115 : write(miss, closesthit, anyhit) : read(miss, closesthit);
+int x116 : write(miss, closesthit, anyhit) : read(anyhit);
+int x117 : write(miss, closesthit, anyhit) : read(miss, anyhit);
+int x118 : write(miss, closesthit, anyhit) : read(closesthit, anyhit);
+int x119 : write(miss, closesthit, anyhit) : read(miss, closesthit, anyhit);
+int x120 : write(miss, closesthit, anyhit) : read(caller);
+int x121 : write(miss, closesthit, anyhit) : read(miss, caller);
+int x122 : write(miss, closesthit, anyhit) : read(closesthit, caller);
+int x123 : write(miss, closesthit, anyhit) : read(miss, closesthit, caller);
+int x124 : write(miss, closesthit, anyhit) : read(anyhit, caller);
+int x125 : write(miss, closesthit, anyhit) : read(miss, anyhit, caller);
+int x126 : write(miss, closesthit, anyhit) : read(closesthit, anyhit, caller);
+int x127 : write(miss, closesthit, anyhit) : read(miss, closesthit, anyhit, caller);
+int x128 : write(caller);
+int x129 : write(caller) : read(miss);
+int x130 : write(caller) : read(closesthit);
+int x131 : write(caller) : read(miss, closesthit);
+int x132 : write(caller) : read(anyhit);
+int x133 : write(caller) : read(miss, anyhit);
+int x134 : write(caller) : read(closesthit, anyhit);
+int x135 : write(caller) : read(miss, closesthit, anyhit);
+int x136 : write(caller) : read(caller);
+int x137 : write(caller) : read(miss, caller);
+int x138 : write(caller) : read(closesthit, caller);
+int x139 : write(caller) : read(miss, closesthit, caller);
+int x140 : write(caller) : read(anyhit, caller);
+int x141 : write(caller) : read(miss, anyhit, caller);
+int x142 : write(caller) : read(closesthit, anyhit, caller);
+int x143 : write(caller) : read(miss, closesthit, anyhit, caller);
+int x144 : write(miss, caller);
+int x145 : write(miss, caller) : read(miss);
+int x146 : write(miss, caller) : read(closesthit);
+int x147 : write(miss, caller) : read(miss, closesthit);
+int x148 : write(miss, caller) : read(anyhit);
+int x149 : write(miss, caller) : read(miss, anyhit);
+int x150 : write(miss, caller) : read(closesthit, anyhit);
+int x151 : write(miss, caller) : read(miss, closesthit, anyhit);
+int x152 : write(miss, caller) : read(caller);
+int x153 : write(miss, caller) : read(miss, caller);
+int x154 : write(miss, caller) : read(closesthit, caller);
+int x155 : write(miss, caller) : read(miss, closesthit, caller);
+int x156 : write(miss, caller) : read(anyhit, caller);
+int x157 : write(miss, caller) : read(miss, anyhit, caller);
+int x158 : write(miss, caller) : read(closesthit, anyhit, caller);
+int x159 : write(miss, caller) : read(miss, closesthit, anyhit, caller);
+int x160 : write(closesthit, caller);
+int x161 : write(closesthit, caller) : read(miss);
+int x162 : write(closesthit, caller) : read(closesthit);
+int x163 : write(closesthit, caller) : read(miss, closesthit);
+int x164 : write(closesthit, caller) : read(anyhit);
+int x165 : write(closesthit, caller) : read(miss, anyhit);
+int x166 : write(closesthit, caller) : read(closesthit, anyhit);
+int x167 : write(closesthit, caller) : read(miss, closesthit, anyhit);
+int x168 : write(closesthit, caller) : read(caller);
+int x169 : write(closesthit, caller) : read(miss, caller);
+int x170 : write(closesthit, caller) : read(closesthit, caller);
+int x171 : write(closesthit, caller) : read(miss, closesthit, caller);
+int x172 : write(closesthit, caller) : read(anyhit, caller);
+int x173 : write(closesthit, caller) : read(miss, anyhit, caller);
+int x174 : write(closesthit, caller) : read(closesthit, anyhit, caller);
+int x175 : write(closesthit, caller) : read(miss, closesthit, anyhit, caller);
+int x176 : write(miss, closesthit, caller);
+int x177 : write(miss, closesthit, caller) : read(miss);
+int x178 : write(miss, closesthit, caller) : read(closesthit);
+int x179 : write(miss, closesthit, caller) : read(miss, closesthit);
+int x180 : write(miss, closesthit, caller) : read(anyhit);
+int x181 : write(miss, closesthit, caller) : read(miss, anyhit);
+int x182 : write(miss, closesthit, caller) : read(closesthit, anyhit);
+int x183 : write(miss, closesthit, caller) : read(miss, closesthit, anyhit);
+int x184 : write(miss, closesthit, caller) : read(caller);
+int x185 : write(miss, closesthit, caller) : read(miss, caller);
+int x186 : write(miss, closesthit, caller) : read(closesthit, caller);
+int x187 : write(miss, closesthit, caller) : read(miss, closesthit, caller);
+int x188 : write(miss, closesthit, caller) : read(anyhit, caller);
+int x189 : write(miss, closesthit, caller) : read(miss, anyhit, caller);
+int x190 : write(miss, closesthit, caller) : read(closesthit, anyhit, caller);
+int x191 : write(miss, closesthit, caller) : read(miss, closesthit, anyhit, caller);
+int x192 : write(anyhit, caller);
+int x193 : write(anyhit, caller) : read(miss);
+int x194 : write(anyhit, caller) : read(closesthit);
+int x195 : write(anyhit, caller) : read(miss, closesthit);
+int x196 : write(anyhit, caller) : read(anyhit);
+int x197 : write(anyhit, caller) : read(miss, anyhit);
+int x198 : write(anyhit, caller) : read(closesthit, anyhit);
+int x199 : write(anyhit, caller) : read(miss, closesthit, anyhit);
+int x200 : write(anyhit, caller) : read(caller);
+int x201 : write(anyhit, caller) : read(miss, caller);
+int x202 : write(anyhit, caller) : read(closesthit, caller);
+int x203 : write(anyhit, caller) : read(miss, closesthit, caller);
+int x204 : write(anyhit, caller) : read(anyhit, caller);
+int x205 : write(anyhit, caller) : read(miss, anyhit, caller);
+int x206 : write(anyhit, caller) : read(closesthit, anyhit, caller);
+int x207 : write(anyhit, caller) : read(miss, closesthit, anyhit, caller);
+int x208 : write(miss, anyhit, caller);
+int x209 : write(miss, anyhit, caller) : read(miss);
+int x210 : write(miss, anyhit, caller) : read(closesthit);
+int x211 : write(miss, anyhit, caller) : read(miss, closesthit);
+int x212 : write(miss, anyhit, caller) : read(anyhit);
+int x213 : write(miss, anyhit, caller) : read(miss, anyhit);
+int x214 : write(miss, anyhit, caller) : read(closesthit, anyhit);
+int x215 : write(miss, anyhit, caller) : read(miss, closesthit, anyhit);
+int x216 : write(miss, anyhit, caller) : read(caller);
+int x217 : write(miss, anyhit, caller) : read(miss, caller);
+int x218 : write(miss, anyhit, caller) : read(closesthit, caller);
+int x219 : write(miss, anyhit, caller) : read(miss, closesthit, caller);
+int x220 : write(miss, anyhit, caller) : read(anyhit, caller);
+int x221 : write(miss, anyhit, caller) : read(miss, anyhit, caller);
+int x222 : write(miss, anyhit, caller) : read(closesthit, anyhit, caller);
+int x223 : write(miss, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+int x224 : write(closesthit, anyhit, caller);
+int x225 : write(closesthit, anyhit, caller) : read(miss);
+int x226 : write(closesthit, anyhit, caller) : read(closesthit);
+int x227 : write(closesthit, anyhit, caller) : read(miss, closesthit);
+int x228 : write(closesthit, anyhit, caller) : read(anyhit);
+int x229 : write(closesthit, anyhit, caller) : read(miss, anyhit);
+int x230 : write(closesthit, anyhit, caller) : read(closesthit, anyhit);
+int x231 : write(closesthit, anyhit, caller) : read(miss, closesthit, anyhit);
+int x232 : write(closesthit, anyhit, caller) : read(caller);
+int x233 : write(closesthit, anyhit, caller) : read(miss, caller);
+int x234 : write(closesthit, anyhit, caller) : read(closesthit, caller);
+int x235 : write(closesthit, anyhit, caller) : read(miss, closesthit, caller);
+int x236 : write(closesthit, anyhit, caller) : read(anyhit, caller);
+int x237 : write(closesthit, anyhit, caller) : read(miss, anyhit, caller);
+int x238 : write(closesthit, anyhit, caller) : read(closesthit, anyhit, caller);
+int x239 : write(closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+int x240 : write(miss, closesthit, anyhit, caller);
+int x241 : write(miss, closesthit, anyhit, caller) : read(miss);
+int x242 : write(miss, closesthit, anyhit, caller) : read(closesthit);
+int x243 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit);
+int x244 : write(miss, closesthit, anyhit, caller) : read(anyhit);
+int x245 : write(miss, closesthit, anyhit, caller) : read(miss, anyhit);
+int x246 : write(miss, closesthit, anyhit, caller) : read(closesthit, anyhit);
+int x247 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit);
+int x248 : write(miss, closesthit, anyhit, caller) : read(caller);
+int x249 : write(miss, closesthit, anyhit, caller) : read(miss, caller);
+int x250 : write(miss, closesthit, anyhit, caller) : read(closesthit, caller);
+int x251 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, caller);
+int x252 : write(miss, closesthit, anyhit, caller) : read(anyhit, caller);
+int x253 : write(miss, closesthit, anyhit, caller) : read(miss, anyhit, caller);
+int x254 : write(miss, closesthit, anyhit, caller) : read(closesthit, anyhit, caller);
+int x255 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+};

+ 21 - 0
tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/extern_call.hlsl

@@ -0,0 +1,21 @@
+// RUN: %dxc -T lib_6_6 %s -enable-payload-qualifiers | FileCheck -input=stderr %s
+
+// CHECK: warning: passing a qualified payload to an extern function can cause undefined behavior if payload qualifiers mismatch
+
+struct [raypayload] Payload
+{
+    int a      : read(closesthit) : write(caller);
+};
+
+struct Attribs
+{
+    float2 barys;
+};
+
+void foo( inout Payload );
+
+[shader("closesthit")]
+void ClosestHitInOut( inout Payload payload, in Attribs attribs )
+{
+    foo(payload); // warn about passing an inout payload to an external function
+}

+ 70 - 0
tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/general.hlsl

@@ -0,0 +1,70 @@
+// RUN: %dxc -T lib_6_x -D TEST_NUM=0 %s | FileCheck -check-prefix=CHK0 %s
+// RUN: %dxc -T lib_6_x -D TEST_NUM=1 %s | FileCheck -check-prefix=CHK1 %s
+// RUN: %dxc -T lib_6_x -D TEST_NUM=2 %s | FileCheck -check-prefix=CHK2 %s
+// RUN: %dxc -T lib_6_x -D TEST_NUM=3 %s | FileCheck -check-prefix=CHK3 %s
+// RUN: %dxc -T lib_6_5 -D TEST_NUM=4 %s -enable-payload-qualifiers | FileCheck -input=stderr -check-prefix=CHK4 %s
+// RUN: %dxc -T lib_6_6 -D TEST_NUM=4 %s | FileCheck -input=stderr -check-prefix=CHK5 %s
+// RUN: %dxc -T lib_6_6 -D TEST_NUM=4 %s -enable-payload-qualifiers | FileCheck -check-prefix=CHK6 %s
+// RUN: %dxc -T lib_6_6 -D TEST_NUM=5 %s -enable-payload-qualifiers | FileCheck -check-prefix=CHK7 %s
+
+// CHK0: error: shader must include inout payload structure parameter.
+// CHK1: error: ray payload parameter must be declared inout
+// CHK2: error: ray payload parameter must be a user defined type with only numeric contents.
+// CHK3: error: ray payload parameter must be a user defined type with only numeric contents.
+
+// check if we get DXIL and the payload type is there 
+// CHK4: Invalid target for payload access qualifiers. Only lib_6_6 and beyond are supported.
+// CHK5: warning: payload access qualifieres are only supported for target lib_6_6 and beyond. You can opt-in for lib_6_6 with the -enable-payload-qualifiers flag. Qualifiers will be dropped.
+// CHK6: %struct.Payload = type { i32, i32 }
+
+// CHK7: error: type 'Payload' used as payload requires that it is annotated with the {{\[[a-z]*\]}} attribute
+
+#if TEST_NUM <= 4
+struct [raypayload] Payload {
+    int a : read(closesthit) : write(caller);
+    int b : write(closesthit) : read(caller);
+};
+#else 
+struct Payload {
+    int a;
+    int b : read (caller) : write(closesthit);
+};
+#endif
+
+// test if compilation fails if payload is not present
+#if TEST_NUM == 0
+[shader("miss")]
+void Miss(){}
+#endif
+
+// test if compilation fails if payload is not inout
+#if TEST_NUM == 1
+[shader("miss")]
+void Miss2( in Payload payload){}
+#endif
+
+// test if compilation fails if payload is not a user defined type
+#if TEST_NUM == 2
+[shader("miss")]
+void Miss3(inout int payload){}
+#endif
+
+#if TEST_NUM == 3
+[shader("miss")]
+void Miss3(inout matrix<float, 2, 2> payload){}
+#endif
+
+// test if compilation fails because not all payload filds are qualified for lib_6_6
+// test if compilation succeeds for lib_6_5 where payload access qualifiers are not required
+#if TEST_NUM == 4
+[shader("miss")]
+void Miss4(inout Payload payload){
+}
+#endif
+
+#if TEST_NUM == 5
+[shader("miss")]
+void Miss5(inout Payload payload){
+    payload.b = 42;
+}
+#endif

+ 266 - 0
tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/metadata.hlsl

@@ -0,0 +1,266 @@
+// RUN: %dxc -T lib_6_6 %s -enable-payload-qualifiers | FileCheck %s
+
+// CHECK: !dx.dxrPayloadAnnotations = !{{{![0-9]+}}}
+// CHECK: {{![0-9]+}} = !{i32 0, %struct.MyPayload undef, {{![0-9]+}}, %struct.SubPayload undef, {{![0-9]+}}}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 0}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 513}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 33}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 545}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8448}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8208}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8464}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12288}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12544}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12304}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12560}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8193}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8449}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8209}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8465}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12289}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12545}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12305}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12561}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8705}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8961}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8721}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8977}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12801}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 13057}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12817}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 13073}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8225}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8481}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8241}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8497}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12321}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12577}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12337}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12593}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8737}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8993}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8753}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 9009}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12833}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 13089}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12849}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 13105}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 258}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 18}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 274}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4098}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4354}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4114}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4370}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 3}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 259}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 19}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 275}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4099}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4355}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4115}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4371}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 515}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 771}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 531}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 787}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4611}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4867}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4627}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4883}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 35}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 291}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 51}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 307}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4131}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4387}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4147}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4403}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 547}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 803}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 563}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 819}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4643}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4899}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4659}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 4915}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8450}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8210}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8466}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12290}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12546}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12306}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12562}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8195}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8451}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8211}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8467}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12291}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12547}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12307}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12563}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8707}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8963}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8723}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8979}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12803}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 13059}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12819}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 13075}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8227}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8483}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8243}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8499}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12323}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12579}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12339}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12595}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8739}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8995}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 8755}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 9011}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12835}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 13091}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 12851}
+// CHECK: {{![0-9]+}} = !{i32 0, i32 13107}
+
+struct [raypayload] SubPayload{
+    int a1 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+    int a2 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+};
+
+struct [raypayload] MyPayload {
+int x0  : write(); 
+int x1  : read();
+int x2  : read() : write();
+int x24 : write(miss) : read(caller);
+int x40 : write(closesthit) : read(caller);
+int x56 : write(miss, closesthit) : read(caller);
+int x65 : write(anyhit) : read(miss);
+int x66 : write(anyhit) : read(closesthit);
+int x67 : write(anyhit) : read(miss, closesthit);
+int x68 : write(anyhit) : read(anyhit);
+int x69 : write(anyhit) : read(miss, anyhit);
+int x70 : write(anyhit) : read(closesthit, anyhit);
+int x71 : write(anyhit) : read(miss, closesthit, anyhit);
+int x72 : write(anyhit) : read(caller);
+int x73 : write(anyhit) : read(miss, caller);
+int x74 : write(anyhit) : read(closesthit, caller);
+int x75 : write(anyhit) : read(miss, closesthit, caller);
+int x76 : write(anyhit) : read(anyhit, caller);
+int x77 : write(anyhit) : read(miss, anyhit, caller);
+int x78 : write(anyhit) : read(closesthit, anyhit, caller);
+int x79 : write(anyhit) : read(miss, closesthit, anyhit, caller);
+int x88 : write(miss, anyhit) : read(caller);
+int x89 : write(miss, anyhit) : read(miss, caller);
+int x90 : write(miss, anyhit) : read(closesthit, caller);
+int x91 : write(miss, anyhit) : read(miss, closesthit, caller);
+int x92 : write(miss, anyhit) : read(anyhit, caller);
+int x93 : write(miss, anyhit) : read(miss, anyhit, caller);
+int x94 : write(miss, anyhit) : read(closesthit, anyhit, caller);
+int x95 : write(miss, anyhit) : read(miss, closesthit, anyhit, caller);
+int x104 : write(closesthit, anyhit) : read(caller);
+int x105 : write(closesthit, anyhit) : read(miss, caller);
+int x106 : write(closesthit, anyhit) : read(closesthit, caller);
+int x107 : write(closesthit, anyhit) : read(miss, closesthit, caller);
+int x108 : write(closesthit, anyhit) : read(anyhit, caller);
+int x109 : write(closesthit, anyhit) : read(miss, anyhit, caller);
+int x110 : write(closesthit, anyhit) : read(closesthit, anyhit, caller);
+int x111 : write(closesthit, anyhit) : read(miss, closesthit, anyhit, caller);
+int x120 : write(miss, closesthit, anyhit) : read(caller);
+int x121 : write(miss, closesthit, anyhit) : read(miss, caller);
+int x122 : write(miss, closesthit, anyhit) : read(closesthit, caller);
+int x123 : write(miss, closesthit, anyhit) : read(miss, closesthit, caller);
+int x124 : write(miss, closesthit, anyhit) : read(anyhit, caller);
+int x125 : write(miss, closesthit, anyhit) : read(miss, anyhit, caller);
+int x126 : write(miss, closesthit, anyhit) : read(closesthit, anyhit, caller);
+int x127 : write(miss, closesthit, anyhit) : read(miss, closesthit, anyhit, caller);
+int x129 : write(caller) : read(miss);
+int x130 : write(caller) : read(closesthit);
+int x131 : write(caller) : read(miss, closesthit);
+int x132 : write(caller) : read(anyhit);
+int x133 : write(caller) : read(miss, anyhit);
+int x134 : write(caller) : read(closesthit, anyhit);
+int x135 : write(caller) : read(miss, closesthit, anyhit);
+int x136 : write(caller) : read(caller);
+int x137 : write(caller) : read(miss, caller);
+int x138 : write(caller) : read(closesthit, caller);
+int x139 : write(caller) : read(miss, closesthit, caller);
+int x140 : write(caller) : read(anyhit, caller);
+int x141 : write(caller) : read(miss, anyhit, caller);
+int x142 : write(caller) : read(closesthit, anyhit, caller);
+int x143 : write(caller) : read(miss, closesthit, anyhit, caller);
+int x152 : write(miss, caller) : read(caller);
+int x153 : write(miss, caller) : read(miss, caller);
+int x154 : write(miss, caller) : read(closesthit, caller);
+int x155 : write(miss, caller) : read(miss, closesthit, caller);
+int x156 : write(miss, caller) : read(anyhit, caller);
+int x157 : write(miss, caller) : read(miss, anyhit, caller);
+int x158 : write(miss, caller) : read(closesthit, anyhit, caller);
+int x159 : write(miss, caller) : read(miss, closesthit, anyhit, caller);
+int x168 : write(closesthit, caller) : read(caller);
+int x169 : write(closesthit, caller) : read(miss, caller);
+int x170 : write(closesthit, caller) : read(closesthit, caller);
+int x171 : write(closesthit, caller) : read(miss, closesthit, caller);
+int x172 : write(closesthit, caller) : read(anyhit, caller);
+int x173 : write(closesthit, caller) : read(miss, anyhit, caller);
+int x174 : write(closesthit, caller) : read(closesthit, anyhit, caller);
+int x175 : write(closesthit, caller) : read(miss, closesthit, anyhit, caller);
+int x184 : write(miss, closesthit, caller) : read(caller);
+int x185 : write(miss, closesthit, caller) : read(miss, caller);
+int x186 : write(miss, closesthit, caller) : read(closesthit, caller);
+int x187 : write(miss, closesthit, caller) : read(miss, closesthit, caller);
+int x188 : write(miss, closesthit, caller) : read(anyhit, caller);
+int x189 : write(miss, closesthit, caller) : read(miss, anyhit, caller);
+int x190 : write(miss, closesthit, caller) : read(closesthit, anyhit, caller);
+int x191 : write(miss, closesthit, caller) : read(miss, closesthit, anyhit, caller);
+int x193 : write(anyhit, caller) : read(miss);
+int x194 : write(anyhit, caller) : read(closesthit);
+int x195 : write(anyhit, caller) : read(miss, closesthit);
+int x196 : write(anyhit, caller) : read(anyhit);
+int x197 : write(anyhit, caller) : read(miss, anyhit);
+int x198 : write(anyhit, caller) : read(closesthit, anyhit);
+int x199 : write(anyhit, caller) : read(miss, closesthit, anyhit);
+int x200 : write(anyhit, caller) : read(caller);
+int x201 : write(anyhit, caller) : read(miss, caller);
+int x202 : write(anyhit, caller) : read(closesthit, caller);
+int x203 : write(anyhit, caller) : read(miss, closesthit, caller);
+int x204 : write(anyhit, caller) : read(anyhit, caller);
+int x205 : write(anyhit, caller) : read(miss, anyhit, caller);
+int x206 : write(anyhit, caller) : read(closesthit, anyhit, caller);
+int x207 : write(anyhit, caller) : read(miss, closesthit, anyhit, caller);
+int x216 : write(miss, anyhit, caller) : read(caller);
+int x217 : write(miss, anyhit, caller) : read(miss, caller);
+int x218 : write(miss, anyhit, caller) : read(closesthit, caller);
+int x219 : write(miss, anyhit, caller) : read(miss, closesthit, caller);
+int x220 : write(miss, anyhit, caller) : read(anyhit, caller);
+int x221 : write(miss, anyhit, caller) : read(miss, anyhit, caller);
+int x222 : write(miss, anyhit, caller) : read(closesthit, anyhit, caller);
+int x223 : write(miss, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+int x232 : write(closesthit, anyhit, caller) : read(caller);
+int x233 : write(closesthit, anyhit, caller) : read(miss, caller);
+int x234 : write(closesthit, anyhit, caller) : read(closesthit, caller);
+int x235 : write(closesthit, anyhit, caller) : read(miss, closesthit, caller);
+int x236 : write(closesthit, anyhit, caller) : read(anyhit, caller);
+int x237 : write(closesthit, anyhit, caller) : read(miss, anyhit, caller);
+int x238 : write(closesthit, anyhit, caller) : read(closesthit, anyhit, caller);
+int x239 : write(closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+int x248 : write(miss, closesthit, anyhit, caller) : read(caller);
+int x249 : write(miss, closesthit, anyhit, caller) : read(miss, caller);
+int x250 : write(miss, closesthit, anyhit, caller) : read(closesthit, caller);
+int x251 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, caller);
+int x252 : write(miss, closesthit, anyhit, caller) : read(anyhit, caller);
+int x253 : write(miss, closesthit, anyhit, caller) : read(miss, anyhit, caller);
+int x254 : write(miss, closesthit, anyhit, caller) : read(closesthit, anyhit, caller);
+int x255 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+
+SubPayload p1;
+
+struct { int x; } s1 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+
+};
+
+[shader("miss")]
+void Miss( inout MyPayload payload ) {
+    payload.x24 = 42;
+}

+ 237 - 0
tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/nested_access.hlsl

@@ -0,0 +1,237 @@
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=0 | FileCheck -check-prefix=CHK0 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=1 | FileCheck -check-prefix=CHK1 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=2 | FileCheck -check-prefix=CHK2 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=3 | FileCheck -check-prefix=CHK3 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=4 | FileCheck -check-prefix=CHK4 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=5 | FileCheck -check-prefix=CHK5 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=6 | FileCheck -check-prefix=CHK6 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=7 | FileCheck -check-prefix=CHK7 -input=stderr  %s
+// RUN: %dxc -T lib_6_6 main %s -enable-payload-qualifiers -D TEST_NUM=8 | FileCheck -check-prefix=CHK8 -input=stderr  %s
+
+// CHK0-NOT: -Wpayload-access-
+
+// CHK1: warning: write will be dropped ('noWrite' is not qualified 'write' for shader stage 'closesthit')
+// CHK1: warning: reading undefined value ('noRead' is not qualified 'read' for shader stage 'closesthit')
+
+// CHK2: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+// CHK2-NOT: warning: reading undefined value ('noRead' is not qualified 'read' for shader stage 'closesthit')
+
+// CHK3: warning: write will be dropped ('noWrite' is not qualified 'write' for shader stage 'closesthit')
+// CHK3: warning: reading undefined value ('noRead' is not qualified 'read' for shader stage 'closesthit')
+// CHK3: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+// CHK3: warning: write will be dropped ('noWrite3' is not qualified 'write' for shader stage 'closesthit')
+// CHK3-NOT: warning: write will be dropped ('noWrite2' is not qualified 'write' for shader stage 'closesthit')
+// CHK3-NOT: warning: reading undefined value ('noRead3' is not qualified 'read' for shader stage 'closesthit')
+// CHK3-NOT: fooload
+
+// CHK4: warning: write will be dropped ('noWrite' is not qualified 'write' for shader stage 'closesthit')
+// CHK4: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+// CHK4: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+
+// CHK5: warning: field 'noWrite' is 'write' for 'caller' stage but field is never written for TraceRay call
+// CHK5: warning: field 'noWrite2' is 'write' for 'caller' stage but field is never written for TraceRay call
+// CHK5: warning: field 'noWrite3' is 'write' for 'caller' stage but field is never written for TraceRay call
+// CHK5: warning: value will be undefined inside TraceRay ('noRead' is not qualified 'write' for 'caller')
+// CHK5: warning: 'noRead' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)
+// CHK5: warning: 'noRead2' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)
+// CHK5: warning: 'noRead3' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)
+// CHK5: warning: 'readWrite' is qualified 'read' for 'caller' but the field is never read after TraceCall (possible performance issue)
+// CHK5: warning: reading undefined value ('noWrite' is returned from TraceRay but not qualified 'read' for 'caller')
+
+// CHK6: warning: potential loss of data for payload field 'clobbered'. Field is qualified 'write' in earlier stages and 'write' only for stage 'closesthit' but never unconditionally written.
+
+// CHK7: warning: potential loss of data for payload field 'clobbered'. Field is qualified 'write' in earlier stages and 'write' only for stage 'anyhit' but never unconditionally written.
+
+// CHK8: warning: write will be dropped ('noWrite' is not qualified 'write' for shader stage 'closesthit')
+// CHK8: warning: reading undefined value ('noRead2' is not qualified 'read' for shader stage 'closesthit')
+// CHK8-NOT: warning: reading undefined value ('noRead3' is not qualified 'read' for shader stage 'closesthit')
+
+struct [raypayload] SubPayload
+{
+    float noRead : write(closesthit) : read(caller); 
+    float noRead2 : write(closesthit) : read(caller); 
+    float noRead3 : write(closesthit) : read(caller); 
+    float noWrite  : read(closesthit) : write(caller); 
+    float noWrite2  : read(closesthit) : write(caller); 
+    float noWrite3  : read(closesthit) : write(caller); 
+    float readWrite : write(closesthit,  caller) : read(caller, closesthit); 
+#if TEST_NUM == 6
+    float clobbered : write(caller, closesthit) : read(caller);
+#endif
+#if TEST_NUM == 7
+    float clobbered : write(anyhit, closesthit) : read(caller);
+#endif
+};
+
+struct [raypayload] Payload
+{
+    SubPayload sub;
+};
+
+struct Attribs { float2 barys; };
+
+
+// Check if no warning is produced if no access happens.
+#if TEST_NUM == 0
+[shader("closesthit")]
+void ClosestHit0( inout Payload payload, in Attribs attribs )
+{
+}
+#endif
+
+// Check if we produce a warning: noRead is undefined
+//                                noWrite is lost
+#if TEST_NUM == 1
+[shader("closesthit")]
+void ClosestHit1( inout Payload payload, in Attribs attribs )
+{
+    payload.sub.noWrite = payload.sub.noRead;
+}
+#endif
+
+// Check if we do not produce warnings for fields dominated by a write.
+// noRead is dominated by a write and thus has a vaild value.
+// noRead2 is not written and undefied because of access qualifiers.
+#if TEST_NUM == 2
+[shader("closesthit")]
+void ClosestHit3( inout Payload payload, in Attribs attribs )
+{
+    payload.sub.noRead = 24;
+    if (payload.sub.noRead)
+        int x = 2 + payload.sub.noRead + payload.sub.noRead2; // warning, read is not dominated by write
+}
+#endif
+
+// Check if we warn in a function that gets the payload as parameter.
+// This should produce warnings on the access to payload but not to fooload.
+// For foo we expect a warning for the read and the write.
+// For foo_in we expect only a warning for the read since the payload is not copy-out.
+// For foo_out we expect only a warning for the write since the payload is not copy-in.
+#if TEST_NUM == 3
+void foo(inout Payload fooload, inout Payload payload)
+{
+    if (fooload.sub.noRead)
+        payload.sub.noWrite = payload.sub.noRead;
+}
+void foo_in(inout Payload fooload, in Payload payload)
+{
+    if (fooload.sub.noRead)
+        payload.sub.noWrite2 = payload.sub.noRead2;
+}
+
+void foo_out(inout Payload fooload, out Payload payload)
+{
+    if (fooload.sub.noRead)
+        payload.sub.noWrite3 = payload.sub.noRead3;
+}
+
+[shader("closesthit")]
+void ClosestHit4( inout Payload payload, in Attribs attribs )
+{
+    Payload fooload;
+    if (payload.sub.readWrite)
+        foo(fooload, payload);
+}
+
+[shader("closesthit")]
+void ClosestHit5( inout Payload payload, in Attribs attribs )
+{
+    Payload fooload;
+    if (payload.sub.readWrite)
+        foo_in(fooload, payload);
+}
+
+[shader("closesthit")]
+void ClosestHit6( inout Payload payload, in Attribs attribs )
+{
+    Payload fooload;
+    if (payload.sub.readWrite)
+        foo_out(fooload, payload);
+}
+#endif
+
+// Check if we don't crash if we have to handle loops and recursion.
+#if TEST_NUM == 4
+void bar(inout Payload payload)
+{
+    payload.sub.noWrite = payload.sub.noRead;
+    bar(payload);
+    payload.sub.noWrite = payload.sub.noRead2;
+}
+
+
+[shader("closesthit")]
+void ClosestHit8( inout Payload payload, in Attribs attribs )
+{
+    payload.sub.noRead = 1;
+    bar(payload);
+    for (int i = 0; i < payload.sub.noRead2; ++i)
+    payload.sub.noWrite = 2;
+}
+#endif
+
+// Test if we produce warnings for TraceRay calls. 
+// In the following the noWrite fields which are write for 'caller' are not written (warn).
+// The noRead field is written to in the caller but is not qualified 'write' warn about lost input.
+// Several fields are marked as 'read' for 'caller' but never read. Warn about potential perf issue.
+// The noWrite field is used after the trace call but the field is not qualified 'read' for 'caller', 
+// the value will be dropped and the read value is undefined (warn).
+#if TEST_NUM == 5
+RaytracingAccelerationStructure scene : register(t0);
+
+[shader("raygeneration")]
+void RayGen1()
+{
+    Payload payload;
+    payload.sub.readWrite = 0;
+    payload.sub.noRead = 0;
+    RayDesc ray;
+    TraceRay( scene, RAY_FLAG_NONE, 0xff, 0, 1, 0, ray, payload );
+
+    if (payload.sub.noWrite)
+    {
+        payload.sub.noWrite = 23;
+    }
+    payload.sub.noWrite = 32;
+}
+#endif
+
+// Check if we produce a warning for a shader that has write access but does not write and 
+// clobbers a field written by an earlier shader stage.
+#if TEST_NUM == 6
+[shader("closesthit")]
+void ClosestHit0( inout Payload payload, in Attribs attribs )
+{
+}
+#endif
+
+// Check if we produce a warning for a shader that has write access but does not write and 
+// clobbers a field written by an earlier shader stage. Here we consider anyhit an earlier stage
+// for anyhit.
+#if TEST_NUM == 7
+[shader("anyhit")]
+void Anyhit0( inout Payload payload, in Attribs attribs  )
+{
+}
+#endif
+
+// Check if a write in a function slience the warning about an undef read in the caller.
+#if TEST_NUM == 8
+void bar(inout Payload payload)
+{
+    payload.sub.noWrite = payload.sub.noRead;
+    bar(payload);
+    payload.sub.noWrite = payload.sub.noRead2;
+    payload.sub.noRead3 = 5;
+}
+
+
+[shader("closesthit")]
+void ClosestHit8( inout Payload payload, in Attribs attribs )
+{
+    payload.sub.noRead = 1;
+    bar(payload);
+    for (int i = 0; i < payload.sub.noRead3; ++i)
+    payload.sub.noWrite = 2;
+}
+#endif

+ 28 - 0
tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/structs.hlsl

@@ -0,0 +1,28 @@
+// RUN: %dxc -T lib_6_6 %s -enable-payload-qualifiers | FileCheck %s
+
+// CHECK: error: payload field 's1' has no payload access qualifiers.
+// CHECK: error: payload field 'p3' is a payload struct. Payload access qualifiers are not allowed on payload types.
+// CHECK: error: payload type 'P1' requires that all fields carry payload access qualifiers.
+
+struct [raypayload] P2 {
+    int c2 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+};
+
+struct S1 {
+    int c1;
+};
+
+struct [raypayload] P1 {
+    int a : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+    int b : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+    S1 s1;
+    S1 s2 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+    P2 p2;
+    P2 p3 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+    matrix <float, 3, 3> matrix1 : write(miss, closesthit, anyhit, caller) : read(miss, closesthit, anyhit, caller);
+};
+
+[shader("miss")]
+void Miss( inout P1 payload )
+{
+}

+ 41 - 0
tools/clang/test/HLSLFileCheck/hlsl/payload_qualifier/trace_calls.hlsl

@@ -0,0 +1,41 @@
+// RUN: %dxc -T lib_6_6 %s -enable-payload-qualifiers -D TEST_NUM=0 %s | FileCheck -check-prefix=CHK0 %s
+// RUN: %dxc -T lib_6_6 %s -enable-payload-qualifiers -D TEST_NUM=1 %s | FileCheck -check-prefix=CHK1 %s
+
+// CHK0: error: type 'Payload' used as payload requires that it is annotated with the [raypayload] attribute
+// CHK1: error: type 'Payload' used as payload requires that it is annotated with the [raypayload] attribute
+
+// Check for payload annotations when payload used on trace.
+
+RaytracingAccelerationStructure scene : register(t0);
+
+struct Payload
+{
+    int a : read (caller, closesthit, miss) : write(caller, closesthit, miss);
+};
+
+struct Attribs
+{
+    float2 barys;
+};
+
+#if TEST_NUM == 0
+[shader("raygeneration")]
+void RayGen()
+{
+    Payload payload_in_rg;
+    RayDesc ray;
+    TraceRay( scene, RAY_FLAG_NONE, 0xff, 0, 1, 0, ray, payload_in_rg );
+
+}
+#endif
+
+#if TEST_NUM == 1
+[shader("closesthit")]
+void Closesthit( inout Payload payload, in Attribs attribs )
+{
+    Payload payload_in_ch;
+    RayDesc ray;
+    TraceRay( scene, RAY_FLAG_NONE, 0xff, 0, 1, 0, ray, payload_in_ch );
+
+}
+#endif

+ 2 - 2
tools/clang/test/HLSLFileCheck/shader_targets/library/lib_default_linkage_6_x.hlsl

@@ -16,8 +16,8 @@ export float export_fn() { return 2.0; }
 static float static_fn() { return 1.0; }
 float defaut_fn() { return 3.0; }
 
-struct Payload {
-  float f;
+struct [raypayload] Payload {
+  float f : read(caller, anyhit) : write(caller, anyhit);
 };
 
 [shader("anyhit")]

+ 2 - 2
tools/clang/test/HLSLFileCheck/shader_targets/library/lib_default_linkage_internal.hlsl

@@ -16,8 +16,8 @@ export float export_fn() { return 2.0; }
 static float static_fn() { return 1.0; }
 float defaut_fn() { return 3.0; }
 
-struct Payload {
-  float f;
+struct [raypayload] Payload {
+  float f : read(caller, anyhit) : write(caller, anyhit);
 };
 
 [shader("anyhit")]

+ 3 - 0
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -1297,6 +1297,8 @@ public:
 
     compiler.getLangOpts().UseMinPrecision = !Opts.Enable16BitTypes;
 
+    compiler.getLangOpts().EnablePayloadAccessQualifiers = Opts.EnablePayloadQualifiers;
+
 // SPIRV change starts
 #ifdef ENABLE_SPIRV_CODEGEN
     compiler.getLangOpts().SPIRV = Opts.GenSPIRV;
@@ -1349,6 +1351,7 @@ public:
     compiler.getCodeGenOpts().HLSLPrintAfterAll = Opts.PrintAfterAll;
     compiler.getCodeGenOpts().HLSLForceZeroStoreLifetimes = Opts.ForceZeroStoreLifetimes;
     compiler.getCodeGenOpts().HLSLEnableLifetimeMarkers = Opts.EnableLifetimeMarkers;
+    compiler.getCodeGenOpts().HLSLEnablePayloadAccessQualifiers = Opts.EnablePayloadQualifiers;
 
     // Translate signature packing options
     if (Opts.PackPrefixStable)

+ 40 - 0
tools/clang/unittests/HLSL/DxilModuleTest.cpp

@@ -68,6 +68,8 @@ public:
 
   TEST_METHOD(SetValidatorVersion)
 
+  TEST_METHOD(PayloadQualifier)
+
   void VerifyValidatorVersionFails(
     LPCWSTR shaderModel, const std::vector<LPCWSTR> &arguments,
     const std::vector<LPCSTR> &expectedErrors);
@@ -578,3 +580,41 @@ TEST_F(DxilModuleTest, SetValidatorVersion) {
   VerifyValidatorVersionFails(L"lib_6_x", {L"-validator-version", L"1.3"}, {
     "Offline library profile cannot be used with non-zero -validator-version."});
 }
+
+TEST_F(DxilModuleTest, PayloadQualifier) {
+  std::vector<LPCWSTR> arguments = { L"-enable-payload-qualifiers" };
+  Compiler c(m_dllSupport);
+
+  LPCSTR shader = "struct [raypayload] Payload\n"
+                  "{\n"
+                  "  double a : read(caller, closesthit, anyhit) : write(caller, miss, closesthit);\n"
+                  "};\n\n"
+                  "[shader(\"miss\")]\n"
+                  "void Miss( inout Payload payload ) { payload.a = 4.2; }\n";
+
+  c.Compile(shader, L"lib_6_6", arguments, {});
+
+  DxilModule &DM = c.GetDxilModule();
+  const DxilTypeSystem &DTS = DM.GetTypeSystem();
+
+  for (auto &p : DTS.GetPayloadAnnotationMap()) {
+    const DxilPayloadAnnotation &plAnnotation = *p.second;
+    for (unsigned i = 0; i < plAnnotation.GetNumFields(); ++i) {
+      const DxilPayloadFieldAnnotation &fieldAnnotation =
+          plAnnotation.GetFieldAnnotation(i);
+      VERIFY_IS_TRUE(fieldAnnotation.HasAnnotations());
+      VERIFY_ARE_EQUAL(DXIL::PayloadAccessQualifier::ReadWrite,
+                       fieldAnnotation.GetPayloadFieldQualifier(
+                           DXIL::PayloadAccessShaderStage::Caller));
+      VERIFY_ARE_EQUAL(DXIL::PayloadAccessQualifier::ReadWrite,
+                       fieldAnnotation.GetPayloadFieldQualifier(
+                           DXIL::PayloadAccessShaderStage::Closesthit));
+      VERIFY_ARE_EQUAL(DXIL::PayloadAccessQualifier::Write,
+                       fieldAnnotation.GetPayloadFieldQualifier(
+                           DXIL::PayloadAccessShaderStage::Miss));
+      VERIFY_ARE_EQUAL(DXIL::PayloadAccessQualifier::Read,
+                       fieldAnnotation.GetPayloadFieldQualifier(
+                           DXIL::PayloadAccessShaderStage::Anyhit));
+    }
+  }
+}