Browse Source

Add SV_ViewID support (#251)

- Add SV_ViewID loaded from intrinsic in Dxil,
  for input to all graphics shader stages
- hctdb: rename shader_models to shader_stages,
  add shader_model for min required shader model
- Validator: validate dxil version required for shader model
- DxilModule: Add GetDxilVersion
- Set Barycentric intrinsics to SM 6.1
- Update SystemValueTest for SM 6.1
Tex Riddell 8 years ago
parent
commit
a581927370

+ 34 - 31
docs/DXIL.rst

@@ -595,37 +595,38 @@ Semantic Interpretations for each SemanticKind at each SigPointKind are as follo
 .. <py::lines('SEMINT-TABLE-RST')>hctdb_instrhelp.get_sem_interpretation_table_rst()</py>
 .. SEMINT-TABLE-RST:BEGIN
 
-====================== ==== ===== ======== ======== ====== ======= ========== ========== ====== ===== ===== ======== ===== ============ ============= ========
-Semantic               VSIn VSOut PCIn     HSIn     HSCPIn HSCPOut PCOut      DSIn       DSCPIn DSOut GSVIn GSIn     GSOut PSIn         PSOut         CSIn
-====================== ==== ===== ======== ======== ====== ======= ========== ========== ====== ===== ===== ======== ===== ============ ============= ========
-Arbitrary              Arb  Arb   NA       NA       Arb    Arb     Arb        Arb        Arb    Arb   Arb   NA       Arb   Arb          NA            NA
-VertexID               SV   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NA            NA
-InstanceID             SV   Arb   NA       NA       Arb    Arb     NA         NA         Arb    Arb   Arb   NA       Arb   Arb          NA            NA
-Position               Arb  SV    NA       NA       SV     SV      Arb        Arb        SV     SV    SV    NA       SV    SV           NA            NA
-RenderTargetArrayIndex Arb  SV    NA       NA       SV     SV      Arb        Arb        SV     SV    SV    NA       SV    SV           NA            NA
-ViewPortArrayIndex     Arb  SV    NA       NA       SV     SV      Arb        Arb        SV     SV    SV    NA       SV    SV           NA            NA
-ClipDistance           Arb  SV    NA       NA       SV     SV      Arb        Arb        SV     SV    SV    NA       SV    SV           NA            NA
-CullDistance           Arb  SV    NA       NA       SV     SV      Arb        Arb        SV     SV    SV    NA       SV    SV           NA            NA
-OutputControlPointID   NA   NA    NA       NotInSig NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NA            NA
-DomainLocation         NA   NA    NA       NA       NA     NA      NA         NotInSig   NA     NA    NA    NA       NA    NA           NA            NA
-PrimitiveID            NA   NA    NotInSig NotInSig NA     NA      NA         NotInSig   NA     NA    NA    Shadow   SGV   SGV          NA            NA
-GSInstanceID           NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NotInSig NA    NA           NA            NA
-SampleIndex            NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    Shadow _41   NA            NA
-IsFrontFace            NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       SGV   SGV          NA            NA
-Coverage               NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NotInSig _50 NotPacked _41 NA
-InnerCoverage          NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NotInSig _50 NA            NA
-Target                 NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           Target        NA
-Depth                  NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NotPacked     NA
-DepthLessEqual         NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NotPacked _50 NA
-DepthGreaterEqual      NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NotPacked _50 NA
-StencilRef             NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NotPacked _50 NA
-DispatchThreadID       NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NA            NotInSig
-GroupID                NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NA            NotInSig
-GroupIndex             NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NA            NotInSig
-GroupThreadID          NA   NA    NA       NA       NA     NA      NA         NA         NA     NA    NA    NA       NA    NA           NA            NotInSig
-TessFactor             NA   NA    NA       NA       NA     NA      TessFactor TessFactor NA     NA    NA    NA       NA    NA           NA            NA
-InsideTessFactor       NA   NA    NA       NA       NA     NA      TessFactor TessFactor NA     NA    NA    NA       NA    NA           NA            NA
-====================== ==== ===== ======== ======== ====== ======= ========== ========== ====== ===== ===== ======== ===== ============ ============= ========
+====================== ============ ===== ============ ============ ====== ======= ========== ============ ====== ===== ===== ============ ===== ============ ============= ========
+Semantic               VSIn         VSOut PCIn         HSIn         HSCPIn HSCPOut PCOut      DSIn         DSCPIn DSOut GSVIn GSIn         GSOut PSIn         PSOut         CSIn
+====================== ============ ===== ============ ============ ====== ======= ========== ============ ====== ===== ===== ============ ===== ============ ============= ========
+Arbitrary              Arb          Arb   NA           NA           Arb    Arb     Arb        Arb          Arb    Arb   Arb   NA           Arb   Arb          NA            NA
+VertexID               SV           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NA            NA
+InstanceID             SV           Arb   NA           NA           Arb    Arb     NA         NA           Arb    Arb   Arb   NA           Arb   Arb          NA            NA
+Position               Arb          SV    NA           NA           SV     SV      Arb        Arb          SV     SV    SV    NA           SV    SV           NA            NA
+RenderTargetArrayIndex Arb          SV    NA           NA           SV     SV      Arb        Arb          SV     SV    SV    NA           SV    SV           NA            NA
+ViewPortArrayIndex     Arb          SV    NA           NA           SV     SV      Arb        Arb          SV     SV    SV    NA           SV    SV           NA            NA
+ClipDistance           Arb          SV    NA           NA           SV     SV      Arb        Arb          SV     SV    SV    NA           SV    SV           NA            NA
+CullDistance           Arb          SV    NA           NA           SV     SV      Arb        Arb          SV     SV    SV    NA           SV    SV           NA            NA
+OutputControlPointID   NA           NA    NA           NotInSig     NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NA            NA
+DomainLocation         NA           NA    NA           NA           NA     NA      NA         NotInSig     NA     NA    NA    NA           NA    NA           NA            NA
+PrimitiveID            NA           NA    NotInSig     NotInSig     NA     NA      NA         NotInSig     NA     NA    NA    Shadow       SGV   SGV          NA            NA
+GSInstanceID           NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NotInSig     NA    NA           NA            NA
+SampleIndex            NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    Shadow _41   NA            NA
+IsFrontFace            NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           SGV   SGV          NA            NA
+Coverage               NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NotInSig _50 NotPacked _41 NA
+InnerCoverage          NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NotInSig _50 NA            NA
+Target                 NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           Target        NA
+Depth                  NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NotPacked     NA
+DepthLessEqual         NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NotPacked _50 NA
+DepthGreaterEqual      NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NotPacked _50 NA
+StencilRef             NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NotPacked _50 NA
+DispatchThreadID       NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NA            NotInSig
+GroupID                NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NA            NotInSig
+GroupIndex             NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NA            NotInSig
+GroupThreadID          NA           NA    NA           NA           NA     NA      NA         NA           NA     NA    NA    NA           NA    NA           NA            NotInSig
+TessFactor             NA           NA    NA           NA           NA     NA      TessFactor TessFactor   NA     NA    NA    NA           NA    NA           NA            NA
+InsideTessFactor       NA           NA    NA           NA           NA     NA      TessFactor TessFactor   NA     NA    NA    NA           NA    NA           NA            NA
+ViewID                 NotInSig _61 NA    NotInSig _61 NotInSig _61 NA     NA      NA         NotInSig _61 NA     NA    NA    NotInSig _61 NA    NotInSig _61 NA            NA
+====================== ============ ===== ============ ============ ====== ======= ========== ============ ====== ===== ===== ============ ===== ============ ============= ========
 
 .. SEMINT-TABLE-RST:END
 
@@ -2044,6 +2045,7 @@ ID  Name                           Description
 139 BarycentricsSampleIndex_       return weights at the location of the sample specified by index
 140 BarycentricsSnapped_           return weights at the location specified in the pixel's 16x16 sample grid
 141 AttributeAtVertex_             returns the values of the attributes at the vertex.
+142 ViewID_                        returns the view index
 === ============================== =================================================================================================================
 
 
@@ -2897,6 +2899,7 @@ SM.COUNTERONLYONSTRUCTBUF             BufferUpdateCounter valid only on structur
 SM.CSNORETURN                         Compute shaders can't return values, outputs must be written in writable resources (UAVs).
 SM.DOMAINLOCATIONIDXOOB               DomainLocation component index out of bounds for the domain.
 SM.DSINPUTCONTROLPOINTCOUNTRANGE      DS input control point count must be [0..%0].  %1 specified
+SM.DXILVERSION                        Target shader model requires specific Dxil Version
 SM.GSINSTANCECOUNTRANGE               GS instance count must be [1..%0].  %1 specified
 SM.GSOUTPUTVERTEXCOUNTRANGE           GS output vertex count must be [0..%0].  %1 specified
 SM.GSTOTALOUTPUTVERTEXDATARANGE       Declared output vertex count (%0) multiplied by the total number of declared scalar components of output data (%1) equals %2.  This value cannot be greater than %3

+ 9 - 2
include/dxc/HLSL/DxilConstants.h

@@ -148,6 +148,7 @@ namespace DXIL {
     GroupThreadID,
     TessFactor,
     InsideTessFactor,
+    ViewID,
     Invalid,
   };
   // SemanticKind-ENUM:END
@@ -311,6 +312,9 @@ namespace DXIL {
     EmitThenCutStream = 99, // equivalent to an EmitStream followed by a CutStream
     GSInstanceID = 100, // GSInstanceID
   
+    // Graphics shader
+    ViewID = 142, // returns the view index
+  
     // Hull shader
     OutputControlPointID = 107, // OutputControlPointID
     PrimitiveID = 108, // PrimitiveID
@@ -449,7 +453,7 @@ namespace DXIL {
     WaveReadLaneAt = 117, // returns the value from the specified lane
     WaveReadLaneFirst = 118, // returns the value from the first lane
   
-    NumOpCodes = 142 // exclusive last value of enumeration
+    NumOpCodes = 143 // exclusive last value of enumeration
   };
   // OPCODE-ENUM:END
 
@@ -505,6 +509,9 @@ namespace DXIL {
     EmitThenCutStream,
     GSInstanceID,
   
+    // Graphics shader
+    ViewID,
+  
     // Hull shader
     OutputControlPointID,
     PrimitiveID,
@@ -605,7 +612,7 @@ namespace DXIL {
     WaveReadLaneAt,
     WaveReadLaneFirst,
   
-    NumOpClasses = 98 // exclusive last value of enumeration
+    NumOpClasses = 99 // exclusive last value of enumeration
   };
   // OPCODECLASS-ENUM:END
 

+ 16 - 0
include/dxc/HLSL/DxilInstructions.h

@@ -3443,5 +3443,21 @@ struct DxilInst_AttributeAtVertex {
   llvm::Value *get_inputColIndex() const { return Instr->getOperand(3); }
   llvm::Value *get_VertexID() const { return Instr->getOperand(4); }
 };
+
+/// This instruction returns the view index
+struct DxilInst_ViewID {
+  const llvm::Instruction *Instr;
+  // Construction and identification
+  DxilInst_ViewID(llvm::Instruction *pInstr) : Instr(pInstr) {}
+  operator bool() const {
+    return hlsl::OP::IsDxilOpFuncCallInst(Instr, hlsl::OP::OpCode::ViewID);
+  }
+  // Validation support
+  bool isAllowed() const { return true; }
+  bool isArgumentListValid() const {
+    if (1 != llvm::dyn_cast<llvm::CallInst>(Instr)->getNumArgOperands()) return false;
+    return true;
+  }
+};
 // INSTR-HELPER:END
 } // namespace hlsl

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

@@ -49,6 +49,7 @@ public:
   OP *GetOP() const;
   void SetShaderModel(const ShaderModel *pSM);
   const ShaderModel *GetShaderModel() const;
+  void GetDxilVersion(unsigned &DxilMajor, unsigned &DxilMinor) const;
 
   // Entry functions.
   llvm::Function *GetEntryFunction();

+ 1 - 0
include/dxc/HLSL/DxilValidation.h

@@ -173,6 +173,7 @@ enum class ValidationRule : unsigned {
   SmCounterOnlyOnStructBuf, // BufferUpdateCounter valid only on structured buffers
   SmDSInputControlPointCountRange, // DS input control point count must be [0..%0].  %1 specified
   SmDomainLocationIdxOOB, // DomainLocation component index out of bounds for the domain.
+  SmDxilVersion, // Target shader model requires specific Dxil Version
   SmGSInstanceCountRange, // GS instance count must be [1..%0].  %1 specified
   SmGSOutputVertexCountRange, // GS output vertex count must be [0..%0].  %1 specified
   SmGSTotalOutputVertexDataRange, // Declared output vertex count (%0) multiplied by the total number of declared scalar components of output data (%1) equals %2.  This value cannot be greater than %3

+ 1 - 0
lib/HLSL/DxilGenerationPass.cpp

@@ -1098,6 +1098,7 @@ static void replaceInputOutputWithIntrinsic(DXIL::SemanticKind semKind, Value *G
   case Semantic::Kind::SampleIndex:           opcode = OP::OpCode::SampleIndex;           break;
   case Semantic::Kind::Coverage:              opcode = OP::OpCode::Coverage;              break;
   case Semantic::Kind::InnerCoverage:         opcode = OP::OpCode::InnerCoverage;         break;
+  case Semantic::Kind::ViewID:                opcode = OP::OpCode::ViewID;                break;
   default:
     DXASSERT(0, "invalid semantic");
     return;

+ 5 - 0
lib/HLSL/DxilModule.cpp

@@ -141,6 +141,11 @@ const ShaderModel *DxilModule::GetShaderModel() const {
   return m_pSM;
 }
 
+void DxilModule::GetDxilVersion(unsigned &DxilMajor, unsigned &DxilMinor) const {
+  DxilMajor = m_DxilMajor;
+  DxilMinor = m_DxilMinor;
+}
+
 Function *DxilModule::GetEntryFunction() {
   return m_pEntryFunc;
 }

+ 7 - 0
lib/HLSL/DxilOperations.cpp

@@ -245,6 +245,9 @@ const OP::OpCodeProperty OP::m_OpCodeProps[(unsigned)OP::OpCode::NumOpCodes] = {
   {  OC::BarycentricsSampleIndex, "BarycentricsSampleIndex",  OCC::BarycentricsSampleIndex,  "barycentricsSampleIndex",    false, false,  true, false, false, false, false, false, false, Attribute::ReadNone, },
   {  OC::BarycentricsSnapped,     "BarycentricsSnapped",      OCC::BarycentricsSnapped,      "barycentricsSnapped",        false, false,  true, false, false, false, false, false, false, Attribute::ReadNone, },
   {  OC::AttributeAtVertex,       "AttributeAtVertex",        OCC::AttributeAtVertex,        "attributeAtVertex",          false,  true,  true, false, false, false, false, false, false, Attribute::ReadNone, },
+
+  // Graphics shader                                                                                                        void,     h,     f,     d,    i1,    i8,   i16,   i32,   i64  function attribute
+  {  OC::ViewID,                  "ViewID",                   OCC::ViewID,                   "viewID",                     false, false, false, false, false, false, false,  true, false, Attribute::ReadNone, },
 };
 // OPCODE-OLOADS:END
 
@@ -701,6 +704,9 @@ Function *OP::GetOpFunc(OpCode OpCode, Type *pOverloadType) {
   case OpCode::BarycentricsSampleIndex:A(pF32);     A(pI32); A(pI8);  A(pI32); break;
   case OpCode::BarycentricsSnapped:    A(pF32);     A(pI32); A(pI8);  A(pI32); A(pI32); break;
   case OpCode::AttributeAtVertex:      A(pETy);     A(pI32); A(pI32); A(pI32); A(pI8);  A(pI8);  break;
+
+    // Graphics shader
+  case OpCode::ViewID:                 A(pI32);     A(pI32); break;
   // OPCODE-OLOAD-FUNCS:END
   default: DXASSERT(false, "otherwise unhandled case"); break;
   }
@@ -831,6 +837,7 @@ llvm::Type *OP::GetOverloadType(OpCode OpCode, llvm::Function *F) {
   case OpCode::GSInstanceID:
   case OpCode::OutputControlPointID:
   case OpCode::PrimitiveID:
+  case OpCode::ViewID:
     return IntegerType::get(m_Ctx, 32);
   case OpCode::CalculateLOD:
   case OpCode::DomainLocation:

+ 1 - 0
lib/HLSL/DxilSemantic.cpp

@@ -148,6 +148,7 @@ const Semantic Semantic::ms_SemanticTable[kNumSemanticRecords] = {
   SP(Kind::GroupThreadID,         "SV_GroupThreadID"),
   SP(Kind::TessFactor,            "SV_TessFactor"),
   SP(Kind::InsideTessFactor,      "SV_InsideTessFactor"),
+  SP(Kind::ViewID,                "SV_ViewID"),
   SP(Kind::Invalid,               nullptr),
 };
 

+ 1 - 0
lib/HLSL/DxilShaderModel.cpp

@@ -103,6 +103,7 @@ const ShaderModel *ShaderModel::GetByName(const char *pszName) {
 
 void ShaderModel::GetDxilVersion(unsigned &DxilMajor, unsigned &DxilMinor) const {
   DXASSERT(m_Major == 6, "invalid major");
+  DxilMajor = 1;
   switch (m_Minor) {
   case 0:
     DxilMinor = 0;

+ 30 - 28
lib/HLSL/DxilSigPoint.cpp

@@ -56,40 +56,42 @@ const SigPoint SigPoint::ms_SigPoints[kNumSigPointRecords] = {
 
 // <py::lines('INTERPRETATION-TABLE')>hctdb_instrhelp.get_interpretation_table()</py>
 // INTERPRETATION-TABLE:BEGIN
-//   Semantic,               VSIn, VSOut, PCIn,     HSIn,     HSCPIn, HSCPOut, PCOut,      DSIn,       DSCPIn, DSOut, GSVIn, GSIn,     GSOut, PSIn,         PSOut,         CSIn
+//   Semantic,               VSIn,         VSOut, PCIn,         HSIn,         HSCPIn, HSCPOut, PCOut,      DSIn,         DSCPIn, DSOut, GSVIn, GSIn,         GSOut, PSIn,         PSOut,         CSIn
 #define DO_INTERPRETATION_TABLE(DO) \
-  DO(Arbitrary,              Arb,  Arb,   NA,       NA,       Arb,    Arb,     Arb,        Arb,        Arb,    Arb,   Arb,   NA,       Arb,   Arb,          NA,            NA) \
-  DO(VertexID,               SV,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NA,            NA) \
-  DO(InstanceID,             SV,   Arb,   NA,       NA,       Arb,    Arb,     NA,         NA,         Arb,    Arb,   Arb,   NA,       Arb,   Arb,          NA,            NA) \
-  DO(Position,               Arb,  SV,    NA,       NA,       SV,     SV,      Arb,        Arb,        SV,     SV,    SV,    NA,       SV,    SV,           NA,            NA) \
-  DO(RenderTargetArrayIndex, Arb,  SV,    NA,       NA,       SV,     SV,      Arb,        Arb,        SV,     SV,    SV,    NA,       SV,    SV,           NA,            NA) \
-  DO(ViewPortArrayIndex,     Arb,  SV,    NA,       NA,       SV,     SV,      Arb,        Arb,        SV,     SV,    SV,    NA,       SV,    SV,           NA,            NA) \
-  DO(ClipDistance,           Arb,  SV,    NA,       NA,       SV,     SV,      Arb,        Arb,        SV,     SV,    SV,    NA,       SV,    SV,           NA,            NA) \
-  DO(CullDistance,           Arb,  SV,    NA,       NA,       SV,     SV,      Arb,        Arb,        SV,     SV,    SV,    NA,       SV,    SV,           NA,            NA) \
-  DO(OutputControlPointID,   NA,   NA,    NA,       NotInSig, NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NA,            NA) \
-  DO(DomainLocation,         NA,   NA,    NA,       NA,       NA,     NA,      NA,         NotInSig,   NA,     NA,    NA,    NA,       NA,    NA,           NA,            NA) \
-  DO(PrimitiveID,            NA,   NA,    NotInSig, NotInSig, NA,     NA,      NA,         NotInSig,   NA,     NA,    NA,    Shadow,   SGV,   SGV,          NA,            NA) \
-  DO(GSInstanceID,           NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NotInSig, NA,    NA,           NA,            NA) \
-  DO(SampleIndex,            NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    Shadow _41,   NA,            NA) \
-  DO(IsFrontFace,            NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       SGV,   SGV,          NA,            NA) \
-  DO(Coverage,               NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NotInSig _50, NotPacked _41, NA) \
-  DO(InnerCoverage,          NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NotInSig _50, NA,            NA) \
-  DO(Target,                 NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           Target,        NA) \
-  DO(Depth,                  NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NotPacked,     NA) \
-  DO(DepthLessEqual,         NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NotPacked _50, NA) \
-  DO(DepthGreaterEqual,      NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NotPacked _50, NA) \
-  DO(StencilRef,             NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NotPacked _50, NA) \
-  DO(DispatchThreadID,       NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NA,            NotInSig) \
-  DO(GroupID,                NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NA,            NotInSig) \
-  DO(GroupIndex,             NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NA,            NotInSig) \
-  DO(GroupThreadID,          NA,   NA,    NA,       NA,       NA,     NA,      NA,         NA,         NA,     NA,    NA,    NA,       NA,    NA,           NA,            NotInSig) \
-  DO(TessFactor,             NA,   NA,    NA,       NA,       NA,     NA,      TessFactor, TessFactor, NA,     NA,    NA,    NA,       NA,    NA,           NA,            NA) \
-  DO(InsideTessFactor,       NA,   NA,    NA,       NA,       NA,     NA,      TessFactor, TessFactor, NA,     NA,    NA,    NA,       NA,    NA,           NA,            NA)
+  DO(Arbitrary,              Arb,          Arb,   NA,           NA,           Arb,    Arb,     Arb,        Arb,          Arb,    Arb,   Arb,   NA,           Arb,   Arb,          NA,            NA) \
+  DO(VertexID,               SV,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NA,            NA) \
+  DO(InstanceID,             SV,           Arb,   NA,           NA,           Arb,    Arb,     NA,         NA,           Arb,    Arb,   Arb,   NA,           Arb,   Arb,          NA,            NA) \
+  DO(Position,               Arb,          SV,    NA,           NA,           SV,     SV,      Arb,        Arb,          SV,     SV,    SV,    NA,           SV,    SV,           NA,            NA) \
+  DO(RenderTargetArrayIndex, Arb,          SV,    NA,           NA,           SV,     SV,      Arb,        Arb,          SV,     SV,    SV,    NA,           SV,    SV,           NA,            NA) \
+  DO(ViewPortArrayIndex,     Arb,          SV,    NA,           NA,           SV,     SV,      Arb,        Arb,          SV,     SV,    SV,    NA,           SV,    SV,           NA,            NA) \
+  DO(ClipDistance,           Arb,          SV,    NA,           NA,           SV,     SV,      Arb,        Arb,          SV,     SV,    SV,    NA,           SV,    SV,           NA,            NA) \
+  DO(CullDistance,           Arb,          SV,    NA,           NA,           SV,     SV,      Arb,        Arb,          SV,     SV,    SV,    NA,           SV,    SV,           NA,            NA) \
+  DO(OutputControlPointID,   NA,           NA,    NA,           NotInSig,     NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NA,            NA) \
+  DO(DomainLocation,         NA,           NA,    NA,           NA,           NA,     NA,      NA,         NotInSig,     NA,     NA,    NA,    NA,           NA,    NA,           NA,            NA) \
+  DO(PrimitiveID,            NA,           NA,    NotInSig,     NotInSig,     NA,     NA,      NA,         NotInSig,     NA,     NA,    NA,    Shadow,       SGV,   SGV,          NA,            NA) \
+  DO(GSInstanceID,           NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NotInSig,     NA,    NA,           NA,            NA) \
+  DO(SampleIndex,            NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    Shadow _41,   NA,            NA) \
+  DO(IsFrontFace,            NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           SGV,   SGV,          NA,            NA) \
+  DO(Coverage,               NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NotInSig _50, NotPacked _41, NA) \
+  DO(InnerCoverage,          NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NotInSig _50, NA,            NA) \
+  DO(Target,                 NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           Target,        NA) \
+  DO(Depth,                  NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NotPacked,     NA) \
+  DO(DepthLessEqual,         NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NotPacked _50, NA) \
+  DO(DepthGreaterEqual,      NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NotPacked _50, NA) \
+  DO(StencilRef,             NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NotPacked _50, NA) \
+  DO(DispatchThreadID,       NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NA,            NotInSig) \
+  DO(GroupID,                NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NA,            NotInSig) \
+  DO(GroupIndex,             NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NA,            NotInSig) \
+  DO(GroupThreadID,          NA,           NA,    NA,           NA,           NA,     NA,      NA,         NA,           NA,     NA,    NA,    NA,           NA,    NA,           NA,            NotInSig) \
+  DO(TessFactor,             NA,           NA,    NA,           NA,           NA,     NA,      TessFactor, TessFactor,   NA,     NA,    NA,    NA,           NA,    NA,           NA,            NA) \
+  DO(InsideTessFactor,       NA,           NA,    NA,           NA,           NA,     NA,      TessFactor, TessFactor,   NA,     NA,    NA,    NA,           NA,    NA,           NA,            NA) \
+  DO(ViewID,                 NotInSig _61, NA,    NotInSig _61, NotInSig _61, NA,     NA,      NA,         NotInSig _61, NA,     NA,    NA,    NotInSig _61, NA,    NotInSig _61, NA,            NA)
 // INTERPRETATION-TABLE:END
 
 const VersionedSemanticInterpretation SigPoint::ms_SemanticInterpretationTable[(unsigned)DXIL::SemanticKind::Invalid][(unsigned)SigPoint::Kind::Invalid] = {
 #define _41 ,4,1
 #define _50 ,5,0
+#define _61 ,6,1
 #define DO(k) VersionedSemanticInterpretation(DXIL::SemanticInterpretationKind::k)
 #define DO_ROW(SEM, VSIn, VSOut, PCIn, HSIn, HSCPIn, HSCPOut, PCOut, DSIn, DSCPIn, DSOut, GSVIn, GSIn, GSOut, PSIn, PSOut, CSIn) \
   { DO(VSIn), DO(VSOut), DO(PCIn), DO(HSIn), DO(HSCPIn), DO(HSCPOut), DO(PCOut), DO(DSIn), DO(DSCPIn), DO(DSOut), DO(GSVIn), DO(GSIn), DO(GSOut), DO(PSIn), DO(PSOut), DO(CSIn) },

+ 60 - 14
lib/HLSL/DxilValidation.cpp

@@ -176,7 +176,8 @@ const char *hlsl::GetValidationRuleText(ValidationRule value) {
     case hlsl::ValidationRule::TypesNoMultiDim: return "Only one dimension allowed for array type";
     case hlsl::ValidationRule::TypesI8: return "I8 can only used as immediate value for intrinsic";
     case hlsl::ValidationRule::SmName: return "Unknown shader model '%0'";
-    case hlsl::ValidationRule::SmOpcode: return "Opcode must be defined in target shader model";
+    case hlsl::ValidationRule::SmDxilVersion: return "Shader model requires Dxil Version %0,%1";
+    case hlsl::ValidationRule::SmOpcode: return "Opcode %0 not valid in shader model %1";
     case hlsl::ValidationRule::SmOperand: return "Operand must be defined in target shader model";
     case hlsl::ValidationRule::SmSemantic: return "Semantic '%0' is invalid as %1 %2";
     case hlsl::ValidationRule::SmNoInterpMode: return "Interpolation mode for '%0' is set but should be undefined";
@@ -355,6 +356,7 @@ struct ValidationContext {
   const unsigned kDxilPreciseMDKind;
   const unsigned kLLVMLoopMDKind;
   bool m_bCoverageIn, m_bInnerCoverageIn;
+  unsigned m_DxilMajor, m_DxilMinor;
 
   ValidationContext(Module &llvmModule, Module *DebugModule,
                     DxilModule &dxilModule,
@@ -368,6 +370,7 @@ struct ValidationContext {
         kLLVMLoopMDKind(llvmModule.getContext().getMDKindID("llvm.loop")),
         DiagPrinter(DiagPrn), LastRuleEmit((ValidationRule)-1),
         m_bCoverageIn(false), m_bInnerCoverageIn(false) {
+    DxilMod.GetDxilVersion(m_DxilMajor, m_DxilMinor);
     for (unsigned i = 0; i < DXIL::kNumOutputStreams; i++) {
       hasOutputPosition[i] = false;
       OutputPositionMask[i] = 0;
@@ -556,32 +559,39 @@ static bool ValidateOpcodeInProfile(DXIL::OpCode opcode,
   // Instructions: ThreadId=93, GroupId=94, ThreadIdInGroup=95,
   // FlattenedThreadIdInGroup=96
   if (93 <= op && op <= 96)
-    return pSM->IsCS();
+    return (pSM->IsCS());
   // Instructions: DomainLocation=105
   if (op == 105)
-    return pSM->IsDS();
+    return (pSM->IsDS());
   // Instructions: LoadOutputControlPoint=103, LoadPatchConstant=104
   if (103 <= op && op <= 104)
-    return pSM->IsDS() || pSM->IsHS();
+    return (pSM->IsDS() || pSM->IsHS());
   // Instructions: EmitStream=97, CutStream=98, EmitThenCutStream=99,
   // GSInstanceID=100
   if (97 <= op && op <= 100)
-    return pSM->IsGS();
+    return (pSM->IsGS());
   // Instructions: PrimitiveID=108
   if (op == 108)
-    return pSM->IsGS() || pSM->IsDS() || pSM->IsHS() || pSM->IsPS();
+    return (pSM->IsGS() || pSM->IsDS() || pSM->IsHS() || pSM->IsPS());
   // Instructions: StorePatchConstant=106, OutputControlPointID=107
   if (106 <= op && op <= 107)
-    return pSM->IsHS();
+    return (pSM->IsHS());
   // Instructions: Sample=60, SampleBias=61, SampleCmp=64,
   // RenderTargetGetSamplePosition=76, RenderTargetGetSampleCount=77,
   // CalculateLOD=81, Discard=82, DerivCoarseX=83, DerivCoarseY=84,
   // DerivFineX=85, DerivFineY=86, EvalSnapped=87, EvalSampleIndex=88,
-  // EvalCentroid=89, SampleIndex=90, Coverage=91, InnerCoverage=92,
-  // Barycentrics=137, BarycentricsCentroid=138, BarycentricsSampleIndex=139,
-  // BarycentricsSnapped=140, AttributeAtVertex=141
-  if (60 <= op && op <= 61 || op == 64 || 76 <= op && op <= 77 || 81 <= op && op <= 92 || 137 <= op && op <= 141)
-    return pSM->IsPS();
+  // EvalCentroid=89, SampleIndex=90, Coverage=91, InnerCoverage=92
+  if (60 <= op && op <= 61 || op == 64 || 76 <= op && op <= 77 || 81 <= op && op <= 92)
+    return (pSM->IsPS());
+  // Instructions: Barycentrics=137, BarycentricsCentroid=138,
+  // BarycentricsSampleIndex=139, BarycentricsSnapped=140, AttributeAtVertex=141
+  if (137 <= op && op <= 141)
+    return (pSM->GetMajor() > 6 || (pSM->GetMajor() == 6 && pSM->GetMinor() >= 1))
+        && (pSM->IsPS());
+  // Instructions: ViewID=142
+  if (op == 142)
+    return (pSM->GetMajor() > 6 || (pSM->GetMajor() == 6 && pSM->GetMinor() >= 1))
+        && (pSM->IsVS() || pSM->IsHS() || pSM->IsDS() || pSM->IsGS() || pSM->IsPS());
   return true;
   // VALOPCODESM-TEXT:END
 }
@@ -1925,7 +1935,9 @@ static void ValidateExternalFunction(Function *F, ValidationContext &ValCtx) {
 
     if (!ValidateOpcodeInProfile(dxilOpcode, pSM)) {
       // Opcode not available in profile.
-      ValCtx.EmitInstrError(CI, ValidationRule::SmOpcode);
+      ValCtx.EmitInstrFormatError(CI, ValidationRule::SmOpcode,
+                                  {hlslOP->GetOpCodeName(dxilOpcode),
+                                   pSM->GetName()});
       continue;
     }
 
@@ -2719,6 +2731,27 @@ static void ValidateValidatorVersion(ValidationContext &ValCtx) {
   ValCtx.EmitError(ValidationRule::MetaWellFormed);
 }
 
+static void ValidateDxilVersion(ValidationContext &ValCtx) {
+  Module *pModule = &ValCtx.M;
+  NamedMDNode *pNode = pModule->getNamedMetadata("dx.version");
+  if (pNode && pNode->getNumOperands() == 1) {
+    MDTuple *pVerValues = dyn_cast<MDTuple>(pNode->getOperand(0));
+    if (pVerValues != nullptr && pVerValues->getNumOperands() == 2) {
+      uint64_t majorVer, minorVer;
+      if (GetNodeOperandAsInt(ValCtx, pVerValues, 0, &majorVer) &&
+          GetNodeOperandAsInt(ValCtx, pVerValues, 1, &minorVer)) {
+        // This will need to be updated as dxil major/minor versions evolve,
+        // depending on the degree of compat across versions.
+        if ((majorVer == 1 && minorVer < 2) &&
+            (majorVer == ValCtx.m_DxilMajor && minorVer == ValCtx.m_DxilMinor)) {
+          return;
+        }
+      }
+    }
+  }
+  ValCtx.EmitError(ValidationRule::MetaWellFormed);
+}
+
 static void ValidateMetadata(ValidationContext &ValCtx) {
   Module *pModule = &ValCtx.M;
   const std::string &target = pModule->getTargetTriple();
@@ -2750,11 +2783,24 @@ static void ValidateMetadata(ValidationContext &ValCtx) {
   }
 
   const hlsl::ShaderModel *SM = ValCtx.DxilMod.GetShaderModel();
-  if (!SM->IsValid() || SM->GetMajor() != 6 || SM->GetMinor() != 0) {
+  if (!SM->IsValid() || SM->GetMajor() != 6 || 
+    (SM->GetMinor() != 0 && SM->GetMinor() != 1)) {
     ValCtx.EmitFormatError(ValidationRule::SmName,
                            {ValCtx.DxilMod.GetShaderModel()->GetName()});
   }
 
+  if (SM->GetMajor() == 6) {
+    // Make sure DxilVersion matches the shader model.
+    unsigned SMDxilMajor, SMDxilMinor;
+    SM->GetDxilVersion(SMDxilMajor, SMDxilMinor);
+    if (ValCtx.m_DxilMajor != SMDxilMajor || ValCtx.m_DxilMinor != SMDxilMinor) {
+      ValCtx.EmitFormatError(ValidationRule::SmDxilVersion,
+                             {std::to_string(SMDxilMajor),
+                              std::to_string(SMDxilMinor)});
+    }
+  }
+
+  ValidateDxilVersion(ValCtx);
   ValidateValidatorVersion(ValCtx);
 }
 

+ 1 - 1
tools/clang/test/CodeGenHLSL/attributeAtVertex.hlsl

@@ -1,4 +1,4 @@
-// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s
+// RUN: %dxc -E main -T ps_6_1 %s | FileCheck %s
 
 // CHECK: call float @dx.op.attributeAtVertex.f32(i32 141, i32 0, i32 0, i8 0, i8 0)
 // CHECK: call float @dx.op.attributeAtVertex.f32(i32 141, i32 0, i32 0, i8 1, i8 0)

+ 1 - 1
tools/clang/test/CodeGenHLSL/barycentrics.hlsl

@@ -1,4 +1,4 @@
-// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s
+// RUN: %dxc -E main -T ps_6_1 %s | FileCheck %s
 
 // CHECK: call float @dx.op.barycentrics.f32
 // CHECK: call float @dx.op.barycentricsSampleIndex.f32

+ 1 - 0
tools/clang/test/HLSL/system-values.hlsl

@@ -36,6 +36,7 @@
 #define Def_DomainLocation DECLARE(float2 domainloc : SV_DomainLocation) USE(float, domainloc.x) USE(float, domainloc.y)
 #define Def_OutputControlPointID DECLARE(uint ocpid : SV_OutputControlPointID) USE(uint, ocpid)
 #define Def_GSInstanceID DECLARE(uint gsiid : SV_GSInstanceID) USE(uint, gsiid)
+#define Def_ViewID DECLARE(uint viewID : SV_ViewID) USE(uint, viewID)
 
 #define Domain_Quad 0
 #define Domain_Tri 1

+ 2 - 1
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -1594,7 +1594,8 @@ static const char *OpCodeSignatures[] = {
   "(VertexID)",  // BarycentricsCentroid
   "(VertexID,sampleIndex)",  // BarycentricsSampleIndex
   "(VertexID,offsetX,offsetY)",  // BarycentricsSnapped
-  "(inputSigId,inputRowIndex,inputColIndex,VertexID)"  // AttributeAtVertex
+  "(inputSigId,inputRowIndex,inputColIndex,VertexID)",  // AttributeAtVertex
+  "()"  // ViewID
 };
 // OPCODE-SIGS:END
 

+ 47 - 30
tools/clang/unittests/HLSL/SystemValueTest.cpp

@@ -39,6 +39,9 @@ using namespace std;
 using namespace hlsl_test;
 using namespace hlsl;
 
+static const unsigned HighestMajor = 6;
+static const unsigned HighestMinor = 1;
+
 class SystemValueTest {
 public:
   BEGIN_TEST_CLASS(SystemValueTest)
@@ -63,7 +66,7 @@ public:
   TEST_METHOD(VerifyVersionedSemantics)
   TEST_METHOD(VerifyMissingSemanticFailure)
 
-  void CompileHLSLTemplate(CComPtr<IDxcOperationResult> &pResult, DXIL::SigPointKind sigPointKind, DXIL::SemanticKind semKind, bool addArb, unsigned Major = 6, unsigned Minor = 0) {
+  void CompileHLSLTemplate(CComPtr<IDxcOperationResult> &pResult, DXIL::SigPointKind sigPointKind, DXIL::SemanticKind semKind, bool addArb, unsigned Major = HighestMajor, unsigned Minor = HighestMinor) {
     const Semantic *sem = Semantic::Get(semKind);
     const char* pSemName = sem->GetName();
     std::wstring sigDefValue(L"");
@@ -82,7 +85,7 @@ public:
     return CompileHLSLTemplate(pResult, sigPointKind, sigDefValue, Major, Minor);
   }
 
-  void CompileHLSLTemplate(CComPtr<IDxcOperationResult> &pResult, DXIL::SigPointKind sigPointKind, const std::wstring &sigDefValue, unsigned Major = 6, unsigned Minor = 0) {
+  void CompileHLSLTemplate(CComPtr<IDxcOperationResult> &pResult, DXIL::SigPointKind sigPointKind, const std::wstring &sigDefValue, unsigned Major = HighestMajor, unsigned Minor = HighestMinor) {
     const SigPoint * sigPoint = SigPoint::GetSigPoint(sigPointKind);
     DXIL::ShaderKind shaderKind = sigPoint->GetShaderKind();
 
@@ -98,23 +101,24 @@ public:
       IFT(library->CreateBlobFromFile(path.c_str(), nullptr, &m_pSource));
     }
 
-    std::string entry, profile;
+    static_assert(6 == HighestMajor && 1 == HighestMinor, "otherwise, need to update default profiles below");
+    LPCWSTR entry, profile;
+    wchar_t profile_buf[] = L"vs_6_1";
     switch(shaderKind) {
-      case DXIL::ShaderKind::Vertex: entry = "VSMain"; profile = "vs_6_0"; break;
-      case DXIL::ShaderKind::Pixel: entry = "PSMain"; profile = "ps_6_0"; break;
-      case DXIL::ShaderKind::Geometry: entry = "GSMain"; profile = "gs_6_0"; break;
-      case DXIL::ShaderKind::Hull: entry = "HSMain"; profile = "hs_6_0"; break;
-      case DXIL::ShaderKind::Domain: entry = "DSMain"; profile = "ds_6_0"; break;
-      case DXIL::ShaderKind::Compute: entry = "CSMain"; profile = "cs_6_0"; break;
+      case DXIL::ShaderKind::Vertex:    entry = L"VSMain"; profile = L"vs_6_1"; break;
+      case DXIL::ShaderKind::Pixel:     entry = L"PSMain"; profile = L"ps_6_1"; break;
+      case DXIL::ShaderKind::Geometry:  entry = L"GSMain"; profile = L"gs_6_1"; break;
+      case DXIL::ShaderKind::Hull:      entry = L"HSMain"; profile = L"hs_6_1"; break;
+      case DXIL::ShaderKind::Domain:    entry = L"DSMain"; profile = L"ds_6_1"; break;
+      case DXIL::ShaderKind::Compute:   entry = L"CSMain"; profile = L"cs_6_1"; break;
     }
-    if (Major != 6 || Minor != 0) {
-      llvm::Twine p = llvm::Twine(profile.substr(0, 3)) + llvm::Twine(Major) + " " + llvm::Twine(Minor);
-      profile = p.str();
+    if (Major != HighestMajor || Minor != HighestMinor) {
+      profile_buf[0] = profile[0];
+      profile_buf[3] = L'0' + (wchar_t)Major;
+      profile_buf[5] = L'0' + (wchar_t)Minor;
+      profile = profile_buf;
     }
 
-    CA2W entryW(entry.c_str(), CP_UTF8);
-    CA2W profileW(profile.c_str(), CP_UTF8);
-
     CA2W sigPointNameW(sigPoint->GetName(), CP_UTF8);
     // Strip SV_ from semantic name
     std::wstring sigDefName(sigPointNameW);
@@ -123,8 +127,8 @@ public:
     define.Name = sigDefName.c_str();
     define.Value = sigDefValue.c_str();
 
-    VERIFY_SUCCEEDED(pCompiler->Compile(m_pSource, path.c_str(), entryW,
-                                        profileW, nullptr, 0, &define, 1,
+    VERIFY_SUCCEEDED(pCompiler->Compile(m_pSource, path.c_str(), entry,
+                                        profile, nullptr, 0, &define, 1,
                                         nullptr, &pResult));
   }
 
@@ -210,7 +214,7 @@ TEST_F(SystemValueTest, VerifyNotAvailableFail) {
   WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   for (DXIL::SigPointKind sp = (DXIL::SigPointKind)0; sp < DXIL::SigPointKind::Invalid; sp = (DXIL::SigPointKind)((unsigned)sp + 1)) {
     for (DXIL::SemanticKind sv = (DXIL::SemanticKind)((unsigned)DXIL::SemanticKind::Arbitrary + 1); sv < DXIL::SemanticKind::Invalid; sv = (DXIL::SemanticKind)((unsigned)sv + 1)) {
-      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, 6, 0);
+      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, HighestMajor, HighestMinor);
       if (interpretation == DXIL::SemanticInterpretationKind::NA) {
         CComPtr<IDxcOperationResult> pResult;
         CompileHLSLTemplate(pResult, sp, sv, false);
@@ -237,7 +241,7 @@ TEST_F(SystemValueTest, VerifySVAsArbitrary) {
   WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   for (DXIL::SigPointKind sp = (DXIL::SigPointKind)0; sp < DXIL::SigPointKind::Invalid; sp = (DXIL::SigPointKind)((unsigned)sp + 1)) {
     for (DXIL::SemanticKind sv = (DXIL::SemanticKind)((unsigned)DXIL::SemanticKind::Arbitrary + 1); sv < DXIL::SemanticKind::Invalid; sv = (DXIL::SemanticKind)((unsigned)sv + 1)) {
-      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, 6, 0);
+      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, HighestMajor, HighestMinor);
       if (interpretation == DXIL::SemanticInterpretationKind::Arb) {
         CComPtr<IDxcOperationResult> pResult;
         CompileHLSLTemplate(pResult, sp, sv, false);
@@ -255,7 +259,7 @@ TEST_F(SystemValueTest, VerifySVAsSV) {
   WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   for (DXIL::SigPointKind sp = (DXIL::SigPointKind)0; sp < DXIL::SigPointKind::Invalid; sp = (DXIL::SigPointKind)((unsigned)sp + 1)) {
     for (DXIL::SemanticKind sv = (DXIL::SemanticKind)((unsigned)DXIL::SemanticKind::Arbitrary + 1); sv < DXIL::SemanticKind::Invalid; sv = (DXIL::SemanticKind)((unsigned)sv + 1)) {
-      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, 6, 0);
+      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, HighestMajor, HighestMinor);
       if (interpretation == DXIL::SemanticInterpretationKind::SV || interpretation == DXIL::SemanticInterpretationKind::SGV) {
         CComPtr<IDxcOperationResult> pResult;
         CompileHLSLTemplate(pResult, sp, sv, false);
@@ -273,7 +277,7 @@ TEST_F(SystemValueTest, VerifySGV) {
   WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   for (DXIL::SigPointKind sp = (DXIL::SigPointKind)0; sp < DXIL::SigPointKind::Invalid; sp = (DXIL::SigPointKind)((unsigned)sp + 1)) {
     for (DXIL::SemanticKind sv = (DXIL::SemanticKind)((unsigned)DXIL::SemanticKind::Arbitrary + 1); sv < DXIL::SemanticKind::Invalid; sv = (DXIL::SemanticKind)((unsigned)sv + 1)) {
-      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, 6, 0);
+      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, HighestMajor, HighestMinor);
       if (interpretation == DXIL::SemanticInterpretationKind::SGV) {
         CComPtr<IDxcOperationResult> pResult;
         CompileHLSLTemplate(pResult, sp, sv, true);
@@ -292,7 +296,7 @@ TEST_F(SystemValueTest, VerifySVNotPacked) {
   WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   for (DXIL::SigPointKind sp = (DXIL::SigPointKind)0; sp < DXIL::SigPointKind::Invalid; sp = (DXIL::SigPointKind)((unsigned)sp + 1)) {
     for (DXIL::SemanticKind sv = (DXIL::SemanticKind)((unsigned)DXIL::SemanticKind::Arbitrary + 1); sv < DXIL::SemanticKind::Invalid; sv = (DXIL::SemanticKind)((unsigned)sv + 1)) {
-      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, 6, 0);
+      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, HighestMajor, HighestMinor);
       if (interpretation == DXIL::SemanticInterpretationKind::NotPacked) {
         CComPtr<IDxcOperationResult> pResult;
         CompileHLSLTemplate(pResult, sp, sv, false);
@@ -307,9 +311,10 @@ TEST_F(SystemValueTest, VerifySVNotPacked) {
 }
 
 TEST_F(SystemValueTest, VerifySVNotInSig) {
+  WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   for (DXIL::SigPointKind sp = (DXIL::SigPointKind)0; sp < DXIL::SigPointKind::Invalid; sp = (DXIL::SigPointKind)((unsigned)sp + 1)) {
     for (DXIL::SemanticKind sv = (DXIL::SemanticKind)((unsigned)DXIL::SemanticKind::Arbitrary + 1); sv < DXIL::SemanticKind::Invalid; sv = (DXIL::SemanticKind)((unsigned)sv + 1)) {
-      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, 6, 0);
+      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, HighestMajor, HighestMinor);
       if (interpretation == DXIL::SemanticInterpretationKind::NotInSig) {
         CComPtr<IDxcOperationResult> pResult;
         CompileHLSLTemplate(pResult, sp, sv, false);
@@ -366,9 +371,10 @@ TEST_F(SystemValueTest, VerifyTessFactors) {
 }
 
 TEST_F(SystemValueTest, VerifyShadowEntries) {
+  WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   for (DXIL::SigPointKind sp = (DXIL::SigPointKind)0; sp < DXIL::SigPointKind::Invalid; sp = (DXIL::SigPointKind)((unsigned)sp + 1)) {
     for (DXIL::SemanticKind sv = (DXIL::SemanticKind)((unsigned)DXIL::SemanticKind::Arbitrary + 1); sv < DXIL::SemanticKind::Invalid; sv = (DXIL::SemanticKind)((unsigned)sv + 1)) {
-      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, 6, 0);
+      DXIL::SemanticInterpretationKind interpretation = hlsl::SigPoint::GetInterpretation(sv, sp, HighestMajor, HighestMinor);
       if (interpretation == DXIL::SemanticInterpretationKind::Shadow) {
         CComPtr<IDxcOperationResult> pResult;
         CompileHLSLTemplate(pResult, sp, sv, false);
@@ -383,12 +389,13 @@ TEST_F(SystemValueTest, VerifyShadowEntries) {
 }
 
 TEST_F(SystemValueTest, VerifyVersionedSemantics) {
+  WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   struct TestInfo {
     DXIL::SigPointKind sp;
     DXIL::SemanticKind sv;
     unsigned Major, Minor;
   };
-  const unsigned kNumTests = 7;
+  const unsigned kNumTests = 13;
   TestInfo info[kNumTests] = {
     {DXIL::SigPointKind::PSIn, DXIL::SemanticKind::SampleIndex, 4, 1 },
     {DXIL::SigPointKind::PSIn, DXIL::SemanticKind::Coverage, 5, 0 },
@@ -397,6 +404,12 @@ TEST_F(SystemValueTest, VerifyVersionedSemantics) {
     {DXIL::SigPointKind::PSOut, DXIL::SemanticKind::DepthLessEqual, 5, 0 },
     {DXIL::SigPointKind::PSOut, DXIL::SemanticKind::DepthGreaterEqual, 5, 0 },
     {DXIL::SigPointKind::PSOut, DXIL::SemanticKind::StencilRef, 5, 0 },
+    {DXIL::SigPointKind::VSIn, DXIL::SemanticKind::ViewID, 6, 1 },
+    {DXIL::SigPointKind::HSIn, DXIL::SemanticKind::ViewID, 6, 1 },
+    {DXIL::SigPointKind::PCIn, DXIL::SemanticKind::ViewID, 6, 1 },
+    {DXIL::SigPointKind::DSIn, DXIL::SemanticKind::ViewID, 6, 1 },
+    {DXIL::SigPointKind::GSIn, DXIL::SemanticKind::ViewID, 6, 1 },
+    {DXIL::SigPointKind::PSIn, DXIL::SemanticKind::ViewID, 6, 1 },
   };
 
   for (unsigned i = 0; i < kNumTests; i++) {
@@ -412,7 +425,11 @@ TEST_F(SystemValueTest, VerifyVersionedSemantics) {
     VERIFY_IS_TRUE(SI != DXIL::SemanticInterpretationKind::NA);
     DXIL::SemanticInterpretationKind SILower = hlsl::SigPoint::GetInterpretation(test.sv, test.sp, MajorLower, MinorLower);
     VERIFY_IS_TRUE(SILower == DXIL::SemanticInterpretationKind::NA);
-#ifdef _VERIFY_VERSIONED_SEMANTICS_
+
+    // Don't try compiling to pre-dxil targets:
+    if (MajorLower < 6)
+      continue;
+
     {
       CComPtr<IDxcOperationResult> pResult;
       CompileHLSLTemplate(pResult, test.sp, test.sv, false, test.Major, test.Minor);
@@ -422,21 +439,21 @@ TEST_F(SystemValueTest, VerifyVersionedSemantics) {
     }
     {
       CComPtr<IDxcOperationResult> pResult;
-      CompileHLSLTemplate(pResult, test.sp, test.sv, false, test.Major, test.Minor);
+      CompileHLSLTemplate(pResult, test.sp, test.sv, false, MajorLower, MinorLower);
       HRESULT result;
       VERIFY_SUCCEEDED(pResult->GetStatus(&result));
       VERIFY_FAILED(result);
-      // TODO: Verify error message
       const char *Errors[] = {
-        "TODO: fill this in."
+        "is invalid for shader model",
+        "error: invalid semantic"
       };
       CheckAnyOperationResultMsg(pResult, Errors, _countof(Errors));
     }
-#endif //_VERIFY_VERSIONED_SEMANTICS_
   }
 }
 
 TEST_F(SystemValueTest, VerifyMissingSemanticFailure) {
+  WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
   for (DXIL::SigPointKind sp = (DXIL::SigPointKind)0; sp < DXIL::SigPointKind::Invalid; sp = (DXIL::SigPointKind)((unsigned)sp + 1)) {
     std::wstring sigDefValue(L"Def_Arb_NoSem(uint, arb0)");
     CComPtr<IDxcOperationResult> pResult;

+ 33 - 0
tools/clang/unittests/HLSL/ValidationTest.cpp

@@ -290,6 +290,9 @@ public:
   TEST_METHOD(WhenPSVMismatchThenFail);
   TEST_METHOD(WhenFeatureInfoMismatchThenFail);
 
+  TEST_METHOD(ViewIDInCSFail)
+  TEST_METHOD(ViewIDIn60Fail)
+
   dxc::DxcDllSupport m_dllSupport;
   bool m_CompilerPreservesBBNames;
 
@@ -2888,6 +2891,36 @@ TEST_F(ValidationTest, WhenFeatureInfoMismatchThenFail) {
   );
 }
 
+TEST_F(ValidationTest, ViewIDInCSFail) {
+  RewriteAssemblyCheckMsg(" \
+RWStructuredBuffer<uint> Buf; \
+[numthreads(1,1,1)] \
+void main(uint id : SV_GroupIndex) \
+{ Buf[id] = 0; } \
+    ",
+    "cs_6_1",
+    {"dx.op.flattenedThreadIdInGroup.i32(i32 96",
+     "declare i32 @dx.op.flattenedThreadIdInGroup.i32(i32)"},
+    {"dx.op.viewID.i32(i32 142",
+     "declare i32 @dx.op.viewID.i32(i32)"},
+    "Opcode ViewID not valid in shader model cs_6_1",
+    /*bRegex*/false);
+}
+
+TEST_F(ValidationTest, ViewIDIn60Fail) {
+  RewriteAssemblyCheckMsg(" \
+[domain(\"tri\")] \
+float4 main(float3 pos : Position, uint id : SV_PrimitiveID) : SV_Position \
+{ return float4(pos, id); } \
+    ",
+    "ds_6_0",
+    {"dx.op.primitiveID.i32(i32 108",
+     "declare i32 @dx.op.primitiveID.i32(i32)"},
+    {"dx.op.viewID.i32(i32 142",
+     "declare i32 @dx.op.viewID.i32(i32)"},
+    "Opcode ViewID not valid in shader model ds_6_0",
+    /*bRegex*/false);
+}
 
 
 

+ 27 - 13
utils/hct/hctdb.py

@@ -47,7 +47,8 @@ class db_dxil_inst(object):
         self.is_deriv = False           # whether this is some kind of derivative
         self.is_gradient = False        # whether this requires a gradient calculation
         self.is_wave = False            # whether this requires in-wave, cross-lane functionality
-        self.shader_models = "*"        # shader models to which this applies, * or one or more of cdghpv
+        self.shader_stages = "*"        # shader stages to which this applies, * or one or more of cdghpv
+        self.shader_model = 6,0         # minimum shader model required
         self.inst_helper_prefix = None
         self.fully_qualified_name_prefix = "hlsl::OP::OpCode"
         for k,v in list(kwargs.items()):
@@ -116,7 +117,8 @@ class db_dxil_valrule(object):
         self.err_msg = ""                   # error message associated with rule
         self.category = ""                  # classification for this rule
         self.doc = ""                       # the documentation description of this rule
-        self.shader_models = "*"            # shader models to which this applies, * or one or more of cdghpv
+        self.shader_stages = "*"            # shader stages to which this applies, * or one or more of cdghpv
+        self.shader_model = 6,0             # minimum shader model required
         for k,v in list(kwargs.items()):
             setattr(self, k, v)
 
@@ -205,7 +207,7 @@ class db_dxil(object):
             val = i_val
 
     def populate_categories_and_models(self):
-        "Populate the category and shader_models member of instructions."
+        "Populate the category and shader_stages member of instructions."
         for i in "TempRegLoad,TempRegStore,MinPrecXRegLoad,MinPrecXRegStore,LoadInput,StoreOutput".split(","):
             self.name_idx[i].category = "Temporary, indexable, input, output registers"
         for i in "FAbs,Saturate,IsNaN,IsInf,IsFinite,IsNormal,Cos,Sin,Tan,Acos,Asin,Atan,Hcos,Hsin,Htan,Exp,Frc,Log,Sqrt,Rsqrt".split(","):
@@ -235,29 +237,32 @@ class db_dxil(object):
         for i in "Sample,SampleBias,SampleLevel,SampleGrad,SampleCmp,SampleCmpLevelZero,Texture2DMSGetSamplePosition,RenderTargetGetSamplePosition,RenderTargetGetSampleCount".split(","):
             self.name_idx[i].category = "Resources - sample"
         for i in "Sample,SampleBias,SampleCmp,RenderTargetGetSamplePosition,RenderTargetGetSampleCount".split(","):
-            self.name_idx[i].shader_models = "p"
+            self.name_idx[i].shader_stages = "p"
         for i in "TextureGather,TextureGatherCmp".split(","):
             self.name_idx[i].category = "Resources - gather"
         for i in "AtomicBinOp,AtomicCompareExchange,Barrier".split(","):
             self.name_idx[i].category = "Synchronization"
         for i in "CalculateLOD,Discard,DerivCoarseX,DerivCoarseY,DerivFineX,DerivFineY,EvalSnapped,EvalSampleIndex,EvalCentroid,SampleIndex,Coverage,InnerCoverage,Barycentrics,BarycentricsCentroid,BarycentricsSampleIndex,BarycentricsSnapped,AttributeAtVertex".split(","):
             self.name_idx[i].category = "Pixel shader"
-            self.name_idx[i].shader_models = "p"
+            self.name_idx[i].shader_stages = "p"
         for i in "ThreadId,GroupId,ThreadIdInGroup,FlattenedThreadIdInGroup".split(","):
             self.name_idx[i].category = "Compute shader"
-            self.name_idx[i].shader_models = "c"
+            self.name_idx[i].shader_stages = "c"
         for i in "EmitStream,CutStream,EmitThenCutStream,GSInstanceID".split(","):
             self.name_idx[i].category = "Geometry shader"
-            self.name_idx[i].shader_models = "g"
+            self.name_idx[i].shader_stages = "g"
         for i in "LoadOutputControlPoint,LoadPatchConstant".split(","):
             self.name_idx[i].category = "Domain and hull shader"
-            self.name_idx[i].shader_models = "dh"
+            self.name_idx[i].shader_stages = "dh"
         for i in "DomainLocation".split(","):
             self.name_idx[i].category = "Domain shader"
-            self.name_idx[i].shader_models = "d"
+            self.name_idx[i].shader_stages = "d"
         for i in "StorePatchConstant,OutputControlPointID,PrimitiveID".split(","):
             self.name_idx[i].category = "Hull shader"
-            self.name_idx[i].shader_models = "gdhp" if i == "PrimitiveID" else "h"
+            self.name_idx[i].shader_stages = "gdhp" if i == "PrimitiveID" else "h"
+        for i in "ViewID".split(","):
+            self.name_idx[i].category = "Graphics shader"
+            self.name_idx[i].shader_stages = "vhdgp"
         for i in "MakeDouble,SplitDouble,LegacyDoubleToFloat,LegacyDoubleToSInt32,LegacyDoubleToUInt32".split(","):
             self.name_idx[i].category = "Double precision"
         for i in "CycleCounterLegacy".split(","):
@@ -270,6 +275,8 @@ class db_dxil(object):
                 i.is_wave = True
             elif i.name.startswith("Bitcast"):
                 i.category = "Bitcasts with different sizes"
+        for i in "ViewID,Barycentrics,BarycentricsCentroid,BarycentricsSampleIndex,BarycentricsSnapped,AttributeAtVertex".split(","):
+            self.name_idx[i].shader_model = 6,1
 
     def populate_llvm_instructions(self):
         # Add instructions that map to LLVM instructions.
@@ -1063,7 +1070,11 @@ class db_dxil(object):
             db_dxil_param(5, "i8", "VertexID", "Vertex Index")
         ])
         next_op_idx += 1
-        assert next_op_idx == 142, "next operation index is %d rather than 142 and thus opcodes are broken" % next_op_idx
+        self.add_dxil_op("ViewID", next_op_idx, "ViewID", "returns the view index", "i", "rn", [
+            db_dxil_param(0, "i32", "", "result")])
+        next_op_idx += 1
+
+        assert next_op_idx == 143, "next operation index is %d rather than 143 and thus opcodes are broken" % next_op_idx
 
         # Set interesting properties.
         self.build_indices()
@@ -1368,7 +1379,8 @@ class db_dxil(object):
             (24, "GroupThreadID", ""),
             (25, "TessFactor", ""),
             (26, "InsideTessFactor", ""),
-            (27, "Invalid", ""),
+            (27, "ViewID", ""),
+            (28, "Invalid", ""),
             ])
         self.enums.append(SemanticKind)
         SigPointKind = db_dxil_enum("SigPointKind", "Signature Point is more specific than shader stage or signature as it is unique in both stage and item dimensionality or frequency.", [
@@ -1472,6 +1484,7 @@ class db_dxil(object):
             GroupThreadID,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotInSig
             TessFactor,NA,NA,NA,NA,NA,NA,TessFactor,TessFactor,NA,NA,NA,NA,NA,NA,NA,NA
             InsideTessFactor,NA,NA,NA,NA,NA,NA,TessFactor,TessFactor,NA,NA,NA,NA,NA,NA,NA,NA
+            ViewID,NotInSig _61,NA,NotInSig _61,NotInSig _61,NA,NA,NA,NotInSig _61,NA,NA,NA,NotInSig _61,NA,NotInSig _61,NA,NA
         """
         table = [list(map(str.strip, line.split(','))) for line in SemanticInterpretationCSV.splitlines() if line.strip()]
         for row in table[1:]: assert(len(row) == len(table[0])) # Ensure table is rectangular
@@ -1625,7 +1638,8 @@ class db_dxil(object):
         self.add_valrule("Types.I8", "I8 can only used as immediate value for intrinsic")
 
         self.add_valrule_msg("Sm.Name", "Target shader model name must be known", "Unknown shader model '%0'")
-        self.add_valrule("Sm.Opcode", "Opcode must be defined in target shader model")
+        self.add_valrule_msg("Sm.DxilVersion", "Target shader model requires specific Dxil Version", "Shader model requires Dxil Version %0,%1")
+        self.add_valrule_msg("Sm.Opcode", "Opcode must be defined in target shader model", "Opcode %0 not valid in shader model %1")
         self.add_valrule("Sm.Operand", "Operand must be defined in target shader model")
         self.add_valrule_msg("Sm.Semantic", "Semantic must be defined in target shader model", "Semantic '%0' is invalid as %1 %2")
         self.add_valrule_msg("Sm.NoInterpMode", "Interpolation mode must be undefined for VS input/PS output/patch constant.", "Interpolation mode for '%0' is set but should be undefined")

+ 27 - 22
utils/hct/hctdb_instrhelp.py

@@ -857,35 +857,40 @@ def get_opsigs():
 def get_valopcode_sm_text():
     db = get_db_dxil()
     instrs = [i for i in db.instr if i.is_dxil_op]
-    instrs = sorted(instrs, key=lambda v : v.shader_models + "." + ("%4d" % v.dxil_opid))
+    instrs = sorted(instrs, key=lambda v : (v.shader_model, v.shader_stages, v.dxil_opid))
     last_model = None
-    model_instrs = []
+    last_stage = None
+    grouped_instrs = []
     code = ""
-    def flush_instrs(model_instrs, model_name):
-        if len(model_instrs) == 0:
+    def flush_instrs(grouped_instrs, last_model, last_stage):
+        if len(grouped_instrs) == 0:
             return ""
-        if model_name == "*": # don't write these out, instead fall through
-            return ""
-        result = format_comment("// ", "Instructions: %s" % ", ".join([i.name + "=" + str(i.dxil_opid) for i in model_instrs]))
-        result += "if (" + build_range_code("op", [i.dxil_opid for i in model_instrs]) + ")\n"
+        result = format_comment("// ", "Instructions: %s" % ", ".join([i.name + "=" + str(i.dxil_opid) for i in grouped_instrs]))
+        result += "if (" + build_range_code("op", [i.dxil_opid for i in grouped_instrs]) + ")\n"
         result += "  return "
-        if last_model == "*":
-            result += "true"
+
+        model_cond = stage_cond = None
+        if last_model != (6,0):
+            model_cond = "pSM->GetMajor() > %d || (pSM->GetMajor() == %d && pSM->GetMinor() >= %d)" % (
+                last_model[0], last_model[0], last_model[1])
+        if last_stage != "*":
+            stage_cond = ' || '.join(["pSM->Is%sS()" % c.upper() for c in last_stage])
+        if model_cond or stage_cond:
+            result += '\n      && '.join(
+                ["(%s)" % expr for expr in (model_cond, stage_cond) if expr] )
+            return result + ";\n"
         else:
-            for code_idx,code in enumerate(last_model):
-                if code_idx > 0:
-                    result += " || "
-                result += "pSM->Is" + code.upper() + "S()"
-        result += ";\n"
-        return result
+            # don't write these out, instead fall through
+            return ""
 
     for i in instrs:
-        if i.shader_models != last_model:
-            code += flush_instrs(model_instrs, last_model)
-            model_instrs = []
-            last_model = i.shader_models
-        model_instrs.append(i)
-    code += flush_instrs(model_instrs, last_model)
+        if (i.shader_model, i.shader_stages) != (last_model, last_stage):
+            code += flush_instrs(grouped_instrs, last_model, last_stage)
+            grouped_instrs = []
+            last_model = i.shader_model
+            last_stage = i.shader_stages
+        grouped_instrs.append(i)
+    code += flush_instrs(grouped_instrs, last_model, last_stage)
     code += "return true;\n"
     return code