ソースを参照

(PIX passes) Take constant color from CB (#426)

* maybe?

* Add a "mode" that inserts CB references too

* Add tests

* Xiang's helper to recreate meta-data

* Create CB properly

* Use actual defined constant

* CR feedback: incorrect "one or the other" assert. Unnecessary sethandle. Not accounting for CB id > 0. Test CB id > 0.
Jeff Noyle 8 年 前
コミット
add0d6ec4c

+ 3 - 0
include/dxc/HLSL/DxilMetadataHelper.h

@@ -275,6 +275,7 @@ public:
 
   // Entry points.
   void EmitDxilEntryPoints(std::vector<llvm::MDNode *> &MDEntries);
+  void UpdateDxilEntryPoints(std::vector<llvm::MDNode *> &MDEntries);
   const llvm::NamedMDNode *GetDxilEntryPoints();
   llvm::MDTuple *EmitDxilEntryPointTuple(llvm::Function *pFunc, const std::string &Name, llvm::MDTuple *pSignatures,
                                          llvm::MDTuple *pResources, llvm::MDTuple *pProperties);
@@ -296,6 +297,8 @@ public:
   // Resources.
   llvm::MDTuple *EmitDxilResourceTuple(llvm::MDTuple *pSRVs, llvm::MDTuple *pUAVs, 
                                        llvm::MDTuple *pCBuffers, llvm::MDTuple *pSamplers);
+  void EmitDxilResources(llvm::MDTuple *pDxilResourceTuple);
+  void UpdateDxilResources(llvm::MDTuple *pDxilResourceTuple);
   void GetDxilResources(const llvm::MDOperand &MDO, const llvm::MDTuple *&pSRVs, const llvm::MDTuple *&pUAVs, 
                         const llvm::MDTuple *&pCBuffers, const llvm::MDTuple *&pSamplers);
   void EmitDxilResourceLinkInfoTuple(llvm::MDTuple *pSRVs, llvm::MDTuple *pUAVs,

+ 2 - 0
include/dxc/HLSL/DxilModule.h

@@ -152,6 +152,8 @@ public:
   // DXIL metadata manipulation.
   /// Serialize DXIL in-memory form to metadata form.
   void EmitDxilMetadata();
+  /// Update resource metadata.
+  void ReEmitDxilResources();
   /// Deserialize DXIL metadata form into in-memory form.
   void LoadDxilMetadata();
   /// Check if a Named meta data node is known by dxil module.

+ 35 - 2
lib/HLSL/DxilMetadataHelper.cpp

@@ -204,6 +204,17 @@ void DxilMDHelper::EmitDxilEntryPoints(vector<MDNode *> &MDEntries) {
   }
 }
 
+void DxilMDHelper::UpdateDxilEntryPoints(vector<MDNode *> &MDEntries) {
+  DXASSERT(MDEntries.size() == 1, "only one entry point is supported for now");
+  NamedMDNode *pEntryPointsNamedMD =
+      m_pModule->getNamedMetadata(kDxilEntryPointsMDName);
+  IFTBOOL(pEntryPointsNamedMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
+
+  for (size_t i = 0; i < MDEntries.size(); i++) {
+    pEntryPointsNamedMD->setOperand(i, MDEntries[i]);
+  }
+}
+
 const NamedMDNode *DxilMDHelper::GetDxilEntryPoints() {
   NamedMDNode *pEntryPointsNamedMD = m_pModule->getNamedMetadata(kDxilEntryPointsMDName);
   IFTBOOL(pEntryPointsNamedMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
@@ -439,12 +450,34 @@ MDTuple *DxilMDHelper::EmitDxilResourceTuple(MDTuple *pSRVs, MDTuple *pUAVs,
   MDVals[kDxilResourceSamplers] = pSamplers;
   MDTuple *pTupleMD = MDNode::get(m_Ctx, MDVals);
 
+  return pTupleMD;
+}
+
+void DxilMDHelper::EmitDxilResources(llvm::MDTuple *pDxilResourceTuple) {
   NamedMDNode *pResourcesNamedMD = m_pModule->getNamedMetadata(kDxilResourcesMDName);
   IFTBOOL(pResourcesNamedMD == nullptr, DXC_E_INCORRECT_DXIL_METADATA);
   pResourcesNamedMD = m_pModule->getOrInsertNamedMetadata(kDxilResourcesMDName);
-  pResourcesNamedMD->addOperand(pTupleMD);
+  pResourcesNamedMD->addOperand(pDxilResourceTuple);
+}
 
-  return pTupleMD;
+void DxilMDHelper::UpdateDxilResources(llvm::MDTuple *pDxilResourceTuple) {
+  NamedMDNode *pResourcesNamedMD =
+      m_pModule->getNamedMetadata(kDxilResourcesMDName);
+  if (!pResourcesNamedMD) {
+    pResourcesNamedMD =
+        m_pModule->getOrInsertNamedMetadata(kDxilResourcesMDName);
+  }
+  if (pDxilResourceTuple) {
+    if (pResourcesNamedMD->getNumOperands() != 0) {
+      pResourcesNamedMD->setOperand(0, pDxilResourceTuple);
+    }
+    else {
+      pResourcesNamedMD->addOperand(pDxilResourceTuple);
+    }
+
+  } else {
+    m_pModule->eraseNamedMetadata(pResourcesNamedMD);
+  }
 }
 
 void DxilMDHelper::EmitDxilResourceLinkInfoTuple(MDTuple *pSRVs, MDTuple *pUAVs,

+ 21 - 0
lib/HLSL/DxilModule.cpp

@@ -1193,6 +1193,8 @@ void DxilModule::EmitDxilMetadata() {
 
   MDTuple *pMDSignatures = m_pMDHelper->EmitDxilSignatures(*m_EntrySignature);
   MDTuple *pMDResources = EmitDxilResources();
+  if (pMDResources)
+    m_pMDHelper->EmitDxilResources(pMDResources);
   m_pMDHelper->EmitDxilTypeSystem(GetTypeSystem(), m_LLVMUsed);
   if (!m_pSM->IsCS() &&
       (m_ValMajor > 1 || (m_ValMajor == 1 && m_ValMinor >= 1))) {
@@ -1351,6 +1353,25 @@ MDTuple *DxilModule::EmitDxilResources() {
   }
 }
 
+void DxilModule::ReEmitDxilResources() {
+  MDTuple *pNewResource = EmitDxilResources();
+  m_pMDHelper->UpdateDxilResources(pNewResource);
+  const llvm::NamedMDNode *pEntries = m_pMDHelper->GetDxilEntryPoints();
+  IFTBOOL(pEntries->getNumOperands() == 1, DXC_E_INCORRECT_DXIL_METADATA);
+
+  Function *pEntryFunc;
+  string EntryName;
+  const llvm::MDOperand *pSignatures, *pResources, *pProperties;
+  m_pMDHelper->GetDxilEntryPoint(pEntries->getOperand(0), pEntryFunc, EntryName, pSignatures, pResources, pProperties);
+
+  MDTuple *pMDSig = pSignatures? (MDTuple*)pSignatures->get():nullptr;
+  MDTuple *pMDProperties = pProperties ? (MDTuple*)pProperties->get():nullptr;
+  MDTuple *pEntry = m_pMDHelper->EmitDxilEntryPointTuple(pEntryFunc, EntryName, pMDSig, pNewResource, pMDProperties);
+  vector<MDNode *> Entries;
+  Entries.emplace_back(pEntry);
+  m_pMDHelper->UpdateDxilEntryPoints(Entries);
+}
+
 void DxilModule::LoadDxilResources(const llvm::MDOperand &MDO) {
   if (MDO.get() == nullptr)
     return;

+ 142 - 52
lib/HLSL/DxilOutputColorBecomesConstant.cpp

@@ -29,6 +29,7 @@
 #include "llvm/Transforms/Utils/Local.h"
 #include <memory>
 #include <unordered_set>
+#include <array>
 
 using namespace llvm;
 using namespace hlsl;
@@ -37,17 +38,21 @@ class DxilOutputColorBecomesConstant : public ModulePass {
 
   enum VisualizerInstrumentationMode
   {
-    PRESERVE_ORIGINAL_INSTRUCTIONS,
-    REMOVE_DISCARDS_AND_OPTIONALLY_OTHER_INSTRUCTIONS
+    FromLiteralConstant,
+    FromConstantBuffer
   };
 
   float Red = 1.f;
   float Green = 1.f;
   float Blue = 1.f;
   float Alpha = 1.f;
-  VisualizerInstrumentationMode Mode;
+  VisualizerInstrumentationMode Mode = FromLiteralConstant;
 
-  bool convertTarget0ToConstantValue(Function * OutputFunction, const hlsl::DxilSignature &OutputSignature, OP * HlslOP, float * color);
+  void visitOutputInstructionCallers(
+    Function * OutputFunction, 
+    const hlsl::DxilSignature &OutputSignature, 
+    OP * HlslOP, 
+    std::function<void(CallInst*)> Visitor);
 
 public:
   static char ID; // Pass identification, replacement for typeid
@@ -84,55 +89,35 @@ void DxilOutputColorBecomesConstant::applyOptions(PassOptions O)
   }
 }
 
-bool DxilOutputColorBecomesConstant::convertTarget0ToConstantValue(
-  Function * OutputFunction, 
-  const hlsl::DxilSignature &OutputSignature, 
-  OP * HlslOP, 
-  float * color) {
+void DxilOutputColorBecomesConstant::visitOutputInstructionCallers(
+  Function * OutputFunction,
+  const hlsl::DxilSignature &OutputSignature,
+  OP * HlslOP,
+  std::function<void(CallInst*)> Visitor) {
 
-  bool Modified = false;
-    auto OutputFunctionUses = OutputFunction->uses();
+  auto OutputFunctionUses = OutputFunction->uses();
 
-    for (Use &FunctionUse : OutputFunctionUses) {
-      iterator_range<Value::user_iterator> FunctionUsers = FunctionUse->users();
-      for (User * FunctionUser : FunctionUsers) {
-        if (isa<Instruction>(FunctionUser)) {
-          auto CallInstruction = cast<CallInst>(FunctionUser);
+  for (Use &FunctionUse : OutputFunctionUses) {
+    iterator_range<Value::user_iterator> FunctionUsers = FunctionUse->users();
+    for (User * FunctionUser : FunctionUsers) {
+      if (isa<Instruction>(FunctionUser)) {
+        auto CallInstruction = cast<CallInst>(FunctionUser);
 
-          // Check if the instruction writes to a render target (as opposed to a system-value, such as RenderTargetArrayIndex)
-          Value *OutputID = CallInstruction->getArgOperand(DXIL::OperandIndex::kStoreOutputIDOpIdx);
-          unsigned SignatureElementIndex = cast<ConstantInt>(OutputID)->getLimitedValue();
-          const DxilSignatureElement &SignatureElement = OutputSignature.GetElement(SignatureElementIndex);
+        // Check if the instruction writes to a render target (as opposed to a system-value, such as RenderTargetArrayIndex)
+        Value *OutputID = CallInstruction->getArgOperand(DXIL::OperandIndex::kStoreOutputIDOpIdx);
+        unsigned SignatureElementIndex = cast<ConstantInt>(OutputID)->getLimitedValue();
+        const DxilSignatureElement &SignatureElement = OutputSignature.GetElement(SignatureElementIndex);
 
         // We only modify the output color for RTV0
         if (SignatureElement.GetSemantic()->GetKind() == DXIL::SemanticKind::Target &&
-          SignatureElement.GetSemanticStartIndex() == 0)
-          {
-            // The output column is the channel (red, green, blue or alpha) within the output pixel
-            Value * OutputColumnOperand = CallInstruction->getOperand(hlsl::DXIL::OperandIndex::kStoreOutputColOpIdx);
-            ConstantInt * OutputColumnConstant = cast<ConstantInt>(OutputColumnOperand);
-            APInt OutputColumn = OutputColumnConstant->getValue();
-
-            Value * OutputValueOperand = CallInstruction->getOperand(hlsl::DXIL::OperandIndex::kStoreOutputValOpIdx);
-
-            // Replace the source operand with the appropriate constant literal value
-          if (OutputValueOperand->getType()->isFloatingPointTy())
-            {
-            Modified = true;
-              Constant * FloatConstant = HlslOP->GetFloatConst(color[*OutputColumn.getRawData()]);
-              CallInstruction->setOperand(hlsl::DXIL::OperandIndex::kStoreOutputValOpIdx, FloatConstant);
-            }
-          else if (OutputValueOperand->getType()->isIntegerTy())
-            {
-            Modified = true;
-              Constant * pIntegerConstant = HlslOP->GetI32Const(static_cast<int>(color[*OutputColumn.getRawData()]));
-              CallInstruction->setOperand(hlsl::DXIL::OperandIndex::kStoreOutputValOpIdx, pIntegerConstant);
-            }
-          }
+            SignatureElement.GetSemanticStartIndex() == 0) {
+
+          // Replace the source operand with the appropriate constant value
+          Visitor(CallInstruction);
         }
       }
     }
-  return Modified;
+  }
 }
 
 bool DxilOutputColorBecomesConstant::runOnModule(Module &M)
@@ -140,8 +125,6 @@ bool DxilOutputColorBecomesConstant::runOnModule(Module &M)
   // This pass finds all users of the "StoreOutput" function, and replaces their source operands with a constant
   // value. 
 
-  float color[4] = { Red, Green, Blue, Alpha };
-
   DxilModule &DM = M.GetOrCreateDxilModule();
 
   LLVMContext & Ctx = M.getContext();
@@ -150,18 +133,125 @@ bool DxilOutputColorBecomesConstant::runOnModule(Module &M)
 
   const hlsl::DxilSignature & OutputSignature = DM.GetOutputSignature();
 
+  Function * FloatOutputFunction = HlslOP->GetOpFunc(DXIL::OpCode::StoreOutput, Type::getFloatTy(Ctx));
+  Function * IntOutputFunction = HlslOP->GetOpFunc(DXIL::OpCode::StoreOutput, Type::getInt32Ty(Ctx));
+
+  bool hasFloatOutputs = false;
+  bool hasIntOutputs = false;
+
+  visitOutputInstructionCallers(FloatOutputFunction, OutputSignature, HlslOP, [&hasFloatOutputs](CallInst *) {
+    hasFloatOutputs = true;
+  });
+
+  visitOutputInstructionCallers(IntOutputFunction, OutputSignature, HlslOP, [&hasIntOutputs](CallInst *) {
+    hasIntOutputs = true;
+  });
+
+  if (!hasFloatOutputs && !hasIntOutputs)
+  {
+    return false;
+  }
+
+  // Otherwise, we assume the shader outputs only one or the other (because the 0th RTV can't have a mixed type)
+  DXASSERT(!hasFloatOutputs || !hasIntOutputs, "Only one or the other type of output: float or int");
+
+  std::array<llvm::Value *, 4> ReplacementColors;
+
+  switch (Mode)
+  {
+    case FromLiteralConstant: {
+      if (hasFloatOutputs) {
+        ReplacementColors[0] = HlslOP->GetFloatConst(Red);
+        ReplacementColors[1] = HlslOP->GetFloatConst(Green);
+        ReplacementColors[2] = HlslOP->GetFloatConst(Blue);
+        ReplacementColors[3] = HlslOP->GetFloatConst(Alpha);
+      }
+      if (hasIntOutputs) {
+        ReplacementColors[0] = HlslOP->GetI32Const(static_cast<int>(Red));
+        ReplacementColors[1] = HlslOP->GetI32Const(static_cast<int>(Green));
+        ReplacementColors[2] = HlslOP->GetI32Const(static_cast<int>(Blue));
+        ReplacementColors[3] = HlslOP->GetI32Const(static_cast<int>(Alpha));
+      }
+    }
+    break;
+    case FromConstantBuffer: {
+
+      // Setup a constant buffer with a single float4 in it:
+      SmallVector<llvm::Type*, 4> Elements { Type::getFloatTy(Ctx), Type::getFloatTy(Ctx) , Type::getFloatTy(Ctx) , Type::getFloatTy(Ctx) };
+      llvm::StructType *CBStructTy = llvm::StructType::create(Elements, "PIX_ConstantColorCB_Type");
+      std::unique_ptr<DxilCBuffer> pCBuf = llvm::make_unique<DxilCBuffer>();
+      pCBuf->SetGlobalName("PIX_ConstantColorCBName");
+      pCBuf->SetGlobalSymbol(UndefValue::get(CBStructTy));
+      pCBuf->SetID(0);
+      pCBuf->SetSpaceID((unsigned int)-2); // This is the reserved-for-tools register space
+      pCBuf->SetLowerBound(0);
+      pCBuf->SetRangeSize(1);
+      pCBuf->SetSize(4);
+
+      ID = DM.AddCBuffer(std::move(pCBuf));
+
+      Instruction * entryPointInstruction = &*(DM.GetEntryFunction()->begin()->begin());
+      IRBuilder<> Builder(entryPointInstruction);
+
+      // Create handle for the newly-added constant buffer (which is achieved via a function call)
+      auto ConstantBufferName = "PIX_Constant_Color_CB_Handle";
+      Function *createHandle = HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
+      Constant *CreateHandleOpcodeArg = HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
+      Constant *CBVArg = HlslOP->GetI8Const(static_cast<std::underlying_type<DxilResourceBase::Class>::type>(DXIL::ResourceClass::CBuffer)); 
+      Constant *MetaDataArg = HlslOP->GetU32Const(ID); // position of the metadata record in the corresponding metadata list
+      Constant *IndexArg = HlslOP->GetU32Const(0); // 
+      Constant *FalseArg = HlslOP->GetI1Const(0); // non-uniform resource index: false
+      CallInst *callCreateHandle = Builder.CreateCall(createHandle, { CreateHandleOpcodeArg, CBVArg, MetaDataArg, IndexArg, FalseArg }, ConstantBufferName);
+
+      DM.ReEmitDxilResources();
+
+#define PIX_CONSTANT_VALUE "PIX_Constant_Color_Value"
+
+      // Insert the Buffer load instruction:
+      Function *CBLoad = HlslOP->GetOpFunc(OP::OpCode::CBufferLoadLegacy, hasFloatOutputs ? Type::getFloatTy(Ctx) : Type::getInt32Ty(Ctx));
+      Constant *OpArg = HlslOP->GetU32Const((unsigned)OP::OpCode::CBufferLoadLegacy);
+      Value * ResourceHandle = callCreateHandle;
+      Constant *RowIndex = HlslOP->GetU32Const(0);
+      CallInst *loadLegacy = Builder.CreateCall(CBLoad, { OpArg, ResourceHandle, RowIndex }, PIX_CONSTANT_VALUE);
+
+      // Now extract four color values:
+      ReplacementColors[0] = Builder.CreateExtractValue(loadLegacy, 0, PIX_CONSTANT_VALUE "0");
+      ReplacementColors[1] = Builder.CreateExtractValue(loadLegacy, 1, PIX_CONSTANT_VALUE "1");
+      ReplacementColors[2] = Builder.CreateExtractValue(loadLegacy, 2, PIX_CONSTANT_VALUE "2");
+      ReplacementColors[3] = Builder.CreateExtractValue(loadLegacy, 3, PIX_CONSTANT_VALUE "3");
+    }
+    break;
+    default:
+      assert(false);
+      return 0;
+  }
+
   bool Modified = false;
 
   // The StoreOutput function can store either a float or an integer, depending on the intended output
   // render-target resource view.
-  Function * FloatOutputFunction = HlslOP->GetOpFunc(DXIL::OpCode::StoreOutput, Type::getFloatTy(Ctx));
-  if (FloatOutputFunction->getNumUses() != 0) {
-    Modified = convertTarget0ToConstantValue(FloatOutputFunction, OutputSignature, HlslOP, color);
+  if (hasFloatOutputs) {
+    visitOutputInstructionCallers(FloatOutputFunction, OutputSignature, HlslOP, 
+      [&ReplacementColors, &Modified](CallInst * CallInstruction) {
+      Modified = true;
+      // The output column is the channel (red, green, blue or alpha) within the output pixel
+      Value * OutputColumnOperand = CallInstruction->getOperand(hlsl::DXIL::OperandIndex::kStoreOutputColOpIdx);
+      ConstantInt * OutputColumnConstant = cast<ConstantInt>(OutputColumnOperand);
+      APInt OutputColumn = OutputColumnConstant->getValue();
+      CallInstruction->setOperand(hlsl::DXIL::OperandIndex::kStoreOutputValOpIdx, ReplacementColors[*OutputColumn.getRawData()]);
+    });
   }
 
-  Function * IntOutputFunction = HlslOP->GetOpFunc(DXIL::OpCode::StoreOutput, Type::getInt32Ty(Ctx));
-  if (IntOutputFunction->getNumUses() != 0) {
-    Modified = convertTarget0ToConstantValue(IntOutputFunction, OutputSignature, HlslOP, color);
+  if (hasIntOutputs) {
+    visitOutputInstructionCallers(IntOutputFunction, OutputSignature, HlslOP, 
+    [&ReplacementColors, &Modified](CallInst * CallInstruction) {
+      Modified = true;
+      // The output column is the channel (red, green, blue or alpha) within the output pixel
+      Value * OutputColumnOperand = CallInstruction->getOperand(hlsl::DXIL::OperandIndex::kStoreOutputColOpIdx);
+      ConstantInt * OutputColumnConstant = cast<ConstantInt>(OutputColumnOperand);
+      APInt OutputColumn = OutputColumnConstant->getValue();
+      CallInstruction->setOperand(hlsl::DXIL::OperandIndex::kStoreOutputValOpIdx, ReplacementColors[*OutputColumn.getRawData()]);
+    });
   }
 
   return Modified;

+ 32 - 0
tools/clang/test/HLSL/pix/constantcolorFromCB.hlsl

@@ -0,0 +1,32 @@
+// RUN: %dxc -Emain -Tps_6_0 %s | %opt -S -hlsl-dxil-constantColor,mod-mode=1 | %FileCheck %s
+
+// Check that the CB return type has been added:
+// CHECK: %dx.types.CBufRet.f32 = type { float, float, float, float }
+
+// Look for call to create handle at index 1: --------------------------------------------------------V
+// CHECK: %PIX_Constant_Color_CB_Handle = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 2, i32 1, i32 0, i1 false)
+
+// Look for callto read from CB:
+// CHECK: %PIX_Constant_Color_Value = call %dx.types.CBufRet.f32 @dx.op.cbufferLoadLegacy.f32(i32 59, %dx.types.Handle %PIX_Constant_Color_CB_Handle, i32 0)
+
+// Calls to load elements:
+// CHECK: %PIX_Constant_Color_Value0 = extractvalue %dx.types.CBufRet.f32 %PIX_Constant_Color_Value, 0
+// CHECK: %PIX_Constant_Color_Value1 = extractvalue %dx.types.CBufRet.f32 %PIX_Constant_Color_Value, 1
+// CHECK: %PIX_Constant_Color_Value2 = extractvalue %dx.types.CBufRet.f32 %PIX_Constant_Color_Value, 2
+// CHECK: %PIX_Constant_Color_Value3 = extractvalue %dx.types.CBufRet.f32 %PIX_Constant_Color_Value, 3
+
+// Check that the store-output has been modifed:
+// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0, float %PIX_Constant_Color_Value0)
+// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1, float %PIX_Constant_Color_Value1)
+// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2, float %PIX_Constant_Color_Value2)
+// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3, float %PIX_Constant_Color_Value3)
+
+cbuffer Constants : register(b0)
+{
+  float4 color;
+}
+
+[RootSignature("CBV(b0, space=0, visibility=SHADER_VISIBILITY_PIXEL)")]
+float4 main() : SV_Target {
+  return color;
+}

+ 27 - 0
tools/clang/test/HLSL/pix/constantcolorFromCBint.hlsl

@@ -0,0 +1,27 @@
+// RUN: %dxc -Emain -Tps_6_0 %s | %opt -S -hlsl-dxil-constantColor,mod-mode=1 | %FileCheck %s
+
+// Check that the CB return type has been added:
+// CHECK: %dx.types.CBufRet.i32 = type { i32, i32, i32, i32 }
+
+// Look for call to create handle:
+// CHECK: %PIX_Constant_Color_CB_Handle = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 2, i32 0, i32 0, i1 false)
+
+// Look for callto read from CB:
+// CHECK: %PIX_Constant_Color_Value = call %dx.types.CBufRet.i32 @dx.op.cbufferLoadLegacy.i32(i32 59, %dx.types.Handle %PIX_Constant_Color_CB_Handle, i32 0)
+
+// Calls to load elements:
+// CHECK: %PIX_Constant_Color_Value0 = extractvalue %dx.types.CBufRet.i32 %PIX_Constant_Color_Value, 0
+// CHECK: %PIX_Constant_Color_Value1 = extractvalue %dx.types.CBufRet.i32 %PIX_Constant_Color_Value, 1
+// CHECK: %PIX_Constant_Color_Value2 = extractvalue %dx.types.CBufRet.i32 %PIX_Constant_Color_Value, 2
+// CHECK: %PIX_Constant_Color_Value3 = extractvalue %dx.types.CBufRet.i32 %PIX_Constant_Color_Value, 3
+
+// Check that the store-output has been modifed:
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 0, i32 %PIX_Constant_Color_Value0)
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 1, i32 %PIX_Constant_Color_Value1)
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 2, i32 %PIX_Constant_Color_Value2)
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 3, i32 %PIX_Constant_Color_Value3)
+
+[RootSignature("")]
+int4 main() : SV_Target {
+    return int4(0,0,0,0);
+}

+ 10 - 0
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -377,6 +377,8 @@ public:
   TEST_METHOD(PixConstantColorMRT)
   TEST_METHOD(PixConstantColorUAVs)
   TEST_METHOD(PixConstantColorOtherSIVs)
+  TEST_METHOD(PixConstantColorFromCB)
+  TEST_METHOD(PixConstantColorFromCBint)
 
   TEST_METHOD(CodeGenAbs1)
   TEST_METHOD(CodeGenAbs2)
@@ -2492,6 +2494,14 @@ TEST_F(CompilerTest, PixConstantColorOtherSIVs) {
   CodeGenTestCheck(L"pix\\constantcolorOtherSIVs.hlsl");
 }
 
+TEST_F(CompilerTest, PixConstantColorFromCB) {
+  CodeGenTestCheck(L"pix\\constantcolorFromCB.hlsl");
+}
+
+TEST_F(CompilerTest, PixConstantColorFromCBint) {
+  CodeGenTestCheck(L"pix\\constantcolorFromCBint.hlsl");
+}
+
 TEST_F(CompilerTest, CodeGenAbs1) {
   CodeGenTestCheck(L"..\\CodeGenHLSL\\abs1.hlsl");
 }