瀏覽代碼

[spirv] Change vertex processing stages to emit gl_PerVertex (#757)

As per the Vulkan spec requirement:

  Any variable decorated with Position must be declared as a
  four-component vector of 32-bit floating-point values.

But for HS/DS/GS, we actually have an extra arrayness. If we
generate a stand-alone Postion builtin variable, it will be
an array of float4, which does not comply with the spec.

Similary for the type requirements on ClipDistance and
CullDistance.

The spec could have an problem on this issue, but the GLSL way
is to emit a gl_PerVertex that contains Position, ClipDistance,
and CullDistance. That satisfies the current Vulkan spec.

This commit converts VS output, HS/DS input and output, GS
input to emit the gl_PerVertex struct. It also splits arrays
of structs into arrays of the fields for HS/DS/GS input/output.

ClipDistance/CullDistance is also supported in this commit,
which requires quite some non-trivial handling.
Lei Zhang 7 年之前
父節點
當前提交
734266277b
共有 42 個文件被更改,包括 2822 次插入633 次删除
  1. 171 20
      docs/SPIR-V.rst
  2. 1 0
      tools/clang/lib/SPIRV/CMakeLists.txt
  3. 296 142
      tools/clang/lib/SPIRV/DeclResultIdMapper.cpp
  4. 44 23
      tools/clang/lib/SPIRV/DeclResultIdMapper.h
  5. 759 0
      tools/clang/lib/SPIRV/GlPerVertex.cpp
  6. 203 0
      tools/clang/lib/SPIRV/GlPerVertex.h
  7. 148 90
      tools/clang/lib/SPIRV/SPIRVEmitter.cpp
  8. 7 1
      tools/clang/lib/SPIRV/SPIRVEmitter.h
  9. 26 0
      tools/clang/lib/SPIRV/TypeTranslator.cpp
  10. 11 0
      tools/clang/lib/SPIRV/TypeTranslator.h
  11. 70 47
      tools/clang/test/CodeGenSPIRV/bezier.domain.hlsl2spv
  12. 113 74
      tools/clang/test/CodeGenSPIRV/bezier.hull.hlsl2spv
  13. 0 70
      tools/clang/test/CodeGenSPIRV/hull.output-vars.hlsl
  14. 0 5
      tools/clang/test/CodeGenSPIRV/hull.pcf.input-patch.hlsl
  15. 5 11
      tools/clang/test/CodeGenSPIRV/hull.pcf.output-patch.hlsl
  16. 2 1
      tools/clang/test/CodeGenSPIRV/hull.pcf.primitive-id.1.hlsl
  17. 2 1
      tools/clang/test/CodeGenSPIRV/hull.pcf.primitive-id.2.hlsl
  18. 1 4
      tools/clang/test/CodeGenSPIRV/hull.structure.hlsl
  19. 38 25
      tools/clang/test/CodeGenSPIRV/passthru-vs.hlsl2spv
  20. 1 1
      tools/clang/test/CodeGenSPIRV/semantic.arbitrary.hlsl
  21. 3 2
      tools/clang/test/CodeGenSPIRV/semantic.domain-location.ds.hlsl
  22. 3 2
      tools/clang/test/CodeGenSPIRV/semantic.inside-tess-factor.ds.hlsl
  23. 2 1
      tools/clang/test/CodeGenSPIRV/semantic.inside-tess-factor.hs.hlsl
  24. 2 1
      tools/clang/test/CodeGenSPIRV/semantic.instance-id.vs.hlsl
  25. 2 1
      tools/clang/test/CodeGenSPIRV/semantic.output-control-point-id.hs.hlsl
  26. 0 35
      tools/clang/test/CodeGenSPIRV/semantic.position.ds.hlsl
  27. 0 21
      tools/clang/test/CodeGenSPIRV/semantic.position.gs.hlsl
  28. 0 11
      tools/clang/test/CodeGenSPIRV/semantic.position.ps.hlsl
  29. 0 13
      tools/clang/test/CodeGenSPIRV/semantic.position.vs.hlsl
  30. 2 1
      tools/clang/test/CodeGenSPIRV/semantic.primitive-id.hs.hlsl
  31. 3 2
      tools/clang/test/CodeGenSPIRV/semantic.tess-factor.ds.hlsl
  32. 2 1
      tools/clang/test/CodeGenSPIRV/semantic.tess-factor.hs.hlsl
  33. 2 1
      tools/clang/test/CodeGenSPIRV/semantic.vertex-id.vs.hlsl
  34. 361 0
      tools/clang/test/CodeGenSPIRV/spirv.interface.ds.hlsl
  35. 298 0
      tools/clang/test/CodeGenSPIRV/spirv.interface.hs.hlsl
  36. 85 0
      tools/clang/test/CodeGenSPIRV/spirv.interface.ps.hlsl
  37. 126 0
      tools/clang/test/CodeGenSPIRV/spirv.interface.vs.hlsl
  38. 4 3
      tools/clang/test/CodeGenSPIRV/type.line-stream.hlsl
  39. 1 1
      tools/clang/test/CodeGenSPIRV/type.matrix.hlsl
  40. 3 2
      tools/clang/test/CodeGenSPIRV/type.point-stream.hlsl
  41. 4 3
      tools/clang/test/CodeGenSPIRV/type.triangle-stream.hlsl
  42. 21 17
      tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

+ 171 - 20
docs/SPIR-V.rst

@@ -31,36 +31,134 @@ general graphics ecosystem.
 Note that this document is expected to be an ongoing effort and grow as we
 implement more and more HLSL features.
 
-Vulkan Semantics
-================
+Overview
+========
+
+Although they share the same basic concepts, DirectX and Vulkan are still
+different graphics APIs with semantic gaps. HLSL is the native shading language
+for DirectX, so certain HLSL features do not have corresponding mappings in
+Vulkan, and certain Vulkan specific information does not have native ways to
+express in HLSL source code. This section describes the general translation
+paradigms and how we close some of the major semantic gaps.
 
 Note that the term "semantic" is overloaded. In HLSL, it can mean the string
 attached to shader input or output. For such cases, we refer it as "HLSL
 semantic" or "semantic string". For other cases, we just use the normal
 "semantic" term.
 
-Due to the differences of semantics between DirectX and Vulkan, certain HLSL
-features do not have corresponding mappings in Vulkan, and certain Vulkan
-specific information does not have native ways to express in HLSL source code.
-
-To provide additional information required by Vulkan in HLSL, we need to extend
-the syntax of HLSL.
-`C++ attribute specifier sequence <http://en.cppreference.com/w/cpp/language/attributes>`_
-is a non-intrusive way of achieving such purpose.
-
-For example, to specify the layout of resource variables and the location of
-interface variables:
+Shader entry function
+---------------------
+
+HLSL entry functions can read data from the previous shader stage and write
+data to the next shader stage via function parameters and return value. On the
+contrary, Vulkan requires all SPIR-V entry functions taking no parameters and
+returning void. All data passing between stages should use global variables
+in the ``Input`` and ``Output`` storage class.
+
+To handle this difference, we emit a wrapper function as the SPIR-V entry
+function around the HLSL source code entry function. The wrapper function is
+responsible to read data from SPIR-V ``Input`` global variables and prepare
+them to the types required in the source code entry function signature, call
+the source code entry function, and then decompose the contents in return value
+(and ``out``/``inout`` parameters) to the types required by the SPIR-V
+``Output`` global variables, and then write out. For details about the wrapper
+function, please refer to the `entry function wrapper`_ section.
+
+Shader stage IO interface matching
+----------------------------------
+
+HLSL leverages semantic strings to link variables and pass data between shader
+stages. Great flexibility is allowed as for how to use the semantic strings.
+They can appear on function parameters, function returns, and struct members.
+In Vulkan, linking variables and passing data between shader stages is done via
+numeric ``Location`` decorations on SPIR-V global variables in the ``Input`` and
+``Output`` storage class.
+
+To help handling such differences, we provide `Vulkan specific attributes`_ to
+let the developer to express precisely their intents. The compiler will also try
+its best to deduce the mapping from semantic strings to SPIR-V ``Location``
+numbers when such explicit Vulkan specific attributes are absent. Please see the
+`HLSL semantic and Vulkan Location`_ section for more details about the mapping
+and ``Location`` assignment.
+
+What makes the story complicated is Vulkan's strict requirements on interface
+matching. Basically, a variable in the previous stage is considered a match to
+a variable in the next stage if and only if they are decorated with the same
+``Location`` number and with the exact same type, except for the outermost
+arrayness in hull/domain/geometry shader, which can be ignored regarding
+interface matching. This is causing problems together with the flexibility of
+HLSL semantic strings.
+
+Some HLSL system-value (SV) semantic strings will be mapped into SPIR-V
+variables with builtin decorations, some are not. HLSL non-SV semantic strings
+should all be mapped to SPIR-V variables without builtin decorations (but with
+``Location`` decorations).
+
+With these complications, if we are grouping multiple semantic strings in a
+struct in the HLSL source code, that struct should be flattened and each of
+its members should be mapped separately. For example, for the following:
 
 .. code:: hlsl
 
-  struct S { ... };
+  struct T {
+    float2 clip0 : SV_ClipDistance0;
+    float3 cull0 : SV_CullDistance0;
+    float4 foo   : FOO;
+  };
 
-  [[vk::binding(X, Y)]]
-  StructuredBuffer<S> mySBuffer;
+  struct S {
+    float4 pos   : SV_Position;
+    float2 clip1 : SV_ClipDistance1;
+    float3 cull1 : SV_CullDistance1;
+    float4 bar   : BAR;
+    T      t;
+  };
 
-  [[vk::location(M)]] float4
-  main([[vk::location(N)]] float4 input: A) : B
-  { ... }
+If we have an ``S`` input parameter in pixel shader, we should flatten it
+recursively to generate five SPIR-V ``Input`` variables. Three of them are
+decorated by the ``Position``, ``ClipDistance``, ``CullDistance`` builtin,
+and two of them are decorated by the ``Location`` decoration. (Note that
+``clip0`` and ``clip1`` are concatenated, also ``cull0`` and ``cull1``.
+The ``ClipDistance`` and ``CullDistance`` builtins are special and explained
+in the `gl_PerVertex`_ section.)
+
+Flattening is infective because of Vulkan interface matching rules. If we
+flatten a struct in the output of a previous stage, which may create multiple
+variables decorated with different ``Location`` numbers, we also need to
+flatten it in the input of the next stage. otherwise we may have ``Location``
+mismatch even if we share the same definition of the struct. Because
+hull/domain/geometry shader is optional, we can have different chains of shader
+stages, which means we need to flatten all shader stage interfaces. For
+hull/domain/geometry shader, their inputs/outputs have an additional arrayness.
+So if we are seeing an array of structs in these shaders, we need to flatten
+them into arrays of its fields.
+
+Lastly, to satisfy the type requirements on builtins, after flattening, the
+variables decorated with ``Position``, ``ClipDistance``, and ``CullDistance``
+builtins are grouped into struct, like ``gl_PerVertex`` for certain shader stage
+interface:
+
+============ ===== ======
+Shader Stage Input Output
+============ ===== ======
+    VS         X     G
+    HS         G     G
+    DS         G     G
+    GS         G     S
+    PS         S     X
+============ ===== ======
+
+(``X``: Not applicable, ``G``: Grouped, ``S``: separated)
+
+More details in the `gl_PerVertex`_ section.
+
+Vulkan specific attributes
+--------------------------
+
+To provide additional information required by Vulkan in HLSL, we need to extend
+the syntax of HLSL.
+`C++ attribute specifier sequence <http://en.cppreference.com/w/cpp/language/attributes>`_
+is a non-intrusive way of achieving such purpose.
 
 The namespace ``vk`` will be used for all Vulkan attributes:
 
@@ -78,6 +176,20 @@ Only ``vk::`` attributes in the above list are supported. Other attributes will
 result in warnings and be ignored by the compiler. All C++11 attributes will
 only trigger warnings and be ignored if not compiling towards SPIR-V.
 
+For example, to specify the layout of resource variables and the location of
+interface variables:
+
+.. code:: hlsl
+
+  struct S { ... };
+
+  [[vk::binding(X, Y), vk::counter_binding(Z)]]
+  RWStructuredBuffer<S> mySBuffer;
+
+  [[vk::location(M)]] float4
+  main([[vk::location(N)]] float4 input: A) : B
+  { ... }
+
 HLSL Types
 ==========
 
@@ -525,7 +637,7 @@ Type modifier
 [TODO]
 
 HLSL semantic and Vulkan ``Location``
-------------------------------------
+-------------------------------------
 
 Direct3D uses HLSL "`semantics <https://msdn.microsoft.com/en-us/library/windows/desktop/bb509647(v=vs.85).aspx>`_"
 to compose and match the interfaces between subsequent stages. These semantic
@@ -653,6 +765,45 @@ flattening all structs if structs are used as function parameters or returns.
 There is an exception to the above rule for SV_Target[N]. It will always be
 mapped to ``Location`` number N.
 
+``gl_PerVertex``
+~~~~~~~~~~~~~~~~
+
+Variables annotated with ``SV_Position``, ``SV_ClipDistanceX``, and
+``SV_CullDistanceX`` are mapped into fields of a ``gl_PerVertex`` struct:
+
+.. code:: hlsl
+
+    struct gl_PerVertex {
+        float4 gl_Position;       // SPIR-V BuiltIn Position
+        float  gl_PointSize;      // No HLSL equivalent
+        float  gl_ClipDistance[]; // SPIR-V BuiltIn ClipDistance
+        float  gl_CullDistance[]; // SPIR-V BuiltIn CullDistance
+    };
+
+This mimics how these builtins are handled in GLSL.
+
+Variables decorated with ``SV_ClipDistanceX`` can be float or vector of float
+type. To map them into one float array in the struct, we firstly sort them
+asecendingly according to ``X``, and then concatenate them tightly. For example,
+
+.. code:: hlsl
+
+  struct T {
+    float clip0: SV_ClipDistance0,
+  };
+
+  struct S {
+    float3 clip5: SV_ClipDistance5;
+    ...
+  };
+
+  void main(T t, S s, float2 clip2 : SV_ClipDistance2) { ... }
+
+Then we have an float array of size (1 + 2 + 3 =) 6 for ``ClipDistance``, with
+``clip0`` at offset 0, ``clip2`` at offset 1, ``clip5`` at offset 3.
+
+Variables decorated with ``SV_CullDistanceX`` are mapped similarly as above.
+
 HLSL register and Vulkan binding
 --------------------------------
 

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

@@ -8,6 +8,7 @@ add_clang_library(clangSPIRV
   DeclResultIdMapper.cpp
   Decoration.cpp
   EmitSPIRVAction.cpp
+  GlPerVertex.cpp
   InitListHandler.cpp
   InstBuilderAuto.cpp
   InstBuilderManual.cpp

+ 296 - 142
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -25,14 +25,22 @@ namespace clang {
 namespace spirv {
 
 namespace {
-/// \brief Returns the stage variable's semantic for the given Decl.
-llvm::StringRef getStageVarSemantic(const NamedDecl *decl) {
+/// \brief Returns true if the given decl has a semantic string attached and
+/// writes the info to *semanticStr, *semantic, and *semanticIndex.
+bool getStageVarSemantic(const NamedDecl *decl, llvm::StringRef *semanticStr,
+                         const hlsl::Semantic **semantic,
+                         uint32_t *semanticIndex) {
   for (auto *annotation : decl->getUnusualAnnotations()) {
-    if (auto *semantic = dyn_cast<hlsl::SemanticDecl>(annotation)) {
-      return semantic->SemanticName;
+    if (auto *sema = dyn_cast<hlsl::SemanticDecl>(annotation)) {
+      *semanticStr = sema->SemanticName;
+      llvm::StringRef semanticName;
+      hlsl::Semantic::DecomposeNameAndIndex(*semanticStr, &semanticName,
+                                            semanticIndex);
+      *semantic = hlsl::Semantic::GetByName(semanticName);
+      return true;
     }
   }
-  return {};
+  return false;
 }
 
 /// \brief Returns the stage variable's register assignment for the given Decl.
@@ -63,19 +71,103 @@ bool hasGSPrimitiveTypeQualifier(const Decl *decl) {
           decl->hasAttr<HLSLLineAttr>());
 }
 
+/// \brief Deduces the parameter qualifier for the given decl.
+hlsl::DxilParamInputQual deduceParamQual(const DeclaratorDecl *decl,
+                                         bool asInput) {
+  const auto type = decl->getType();
+
+  if (hlsl::IsHLSLInputPatchType(type))
+    return hlsl::DxilParamInputQual::InputPatch;
+  if (hlsl::IsHLSLOutputPatchType(type))
+    return hlsl::DxilParamInputQual::OutputPatch;
+  // TODO: Add support for multiple output streams.
+  if (hlsl::IsHLSLStreamOutputType(type))
+    return hlsl::DxilParamInputQual::OutStream0;
+
+  // The inputs to the geometry shader that have a primitive type qualifier
+  // must use 'InputPrimitive'.
+  if (hasGSPrimitiveTypeQualifier(decl))
+    return hlsl::DxilParamInputQual::InputPrimitive;
+
+  return asInput ? hlsl::DxilParamInputQual::In : hlsl::DxilParamInputQual::Out;
+}
+
+/// \brief Deduces the HLSL SigPoint for the given decl appearing in the given
+/// shader model.
+const hlsl::SigPoint *deduceSigPoint(const DeclaratorDecl *decl, bool asInput,
+                                     const hlsl::ShaderModel::Kind kind,
+                                     bool isPatchConstant) {
+  return hlsl::SigPoint::GetSigPoint(hlsl::SigPointFromInputQual(
+      deduceParamQual(decl, asInput), kind, isPatchConstant));
+}
+
+/// Returns the type of the given decl. If the given decl is a FunctionDecl,
+/// returns its result type.
+inline QualType getTypeOrFnRetType(const DeclaratorDecl *decl) {
+  if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
+    return funcDecl->getReturnType();
+  }
+  return decl->getType();
+}
 } // anonymous namespace
 
 bool DeclResultIdMapper::createStageOutputVar(const DeclaratorDecl *decl,
                                               uint32_t storedValue,
                                               bool isPatchConstant) {
-  return createStageVars(decl, &storedValue, false, "out.var", isPatchConstant);
+  QualType type = getTypeOrFnRetType(decl);
+
+  // Output stream types (PointStream, LineStream, TriangleStream) are
+  // translated as their underlying struct types.
+  if (hlsl::IsHLSLStreamOutputType(type))
+    type = hlsl::GetHLSLResourceResultType(type);
+
+  const auto *sigPoint = deduceSigPoint(decl, /*asInput=*/false,
+                                        shaderModel.GetKind(), isPatchConstant);
+
+  // HS output variables are created using the other overload. For the rest,
+  // none of them should be created as arrays.
+  assert(sigPoint->GetKind() != hlsl::DXIL::SigPointKind::HSCPOut);
+
+  return createStageVars(decl, sigPoint, /*asInput=*/false, type,
+                         /*arraySize=*/0, llvm::None, &storedValue, "out.var");
+}
+
+bool DeclResultIdMapper::createStageOutputVar(const DeclaratorDecl *decl,
+                                              uint32_t arraySize,
+                                              uint32_t invocationId,
+                                              uint32_t storedValue) {
+  assert(shaderModel.IsHS());
+
+  QualType type = getTypeOrFnRetType(decl);
+
+  const auto *sigPoint =
+      hlsl::SigPoint::GetSigPoint(hlsl::DXIL::SigPointKind::HSCPOut);
+
+  return createStageVars(decl, sigPoint, /*asInput=*/false, type, arraySize,
+                         invocationId, &storedValue, "out.var");
 }
 
 bool DeclResultIdMapper::createStageInputVar(const ParmVarDecl *paramDecl,
                                              uint32_t *loadedValue,
                                              bool isPatchConstant) {
-  return createStageVars(paramDecl, loadedValue, true, "in.var",
-                         isPatchConstant);
+  uint32_t arraySize = 0;
+  QualType type = paramDecl->getType();
+
+  // Deprive the outermost arrayness for HS/DS/GS and use arraySize
+  // to convey that information
+  if (hlsl::IsHLSLInputPatchType(type)) {
+    arraySize = hlsl::GetHLSLInputPatchCount(type);
+    type = hlsl::GetHLSLInputPatchElementType(type);
+  } else if (hlsl::IsHLSLOutputPatchType(type)) {
+    arraySize = hlsl::GetHLSLOutputPatchCount(type);
+    type = hlsl::GetHLSLOutputPatchElementType(type);
+  }
+
+  const auto *sigPoint = deduceSigPoint(paramDecl, /*asInput=*/true,
+                                        shaderModel.GetKind(), isPatchConstant);
+
+  return createStageVars(paramDecl, sigPoint, /*asInput=*/true, type, arraySize,
+                         llvm::None, loadedValue, "in.var");
 }
 
 const DeclResultIdMapper::DeclSpirvInfo *
@@ -307,6 +399,11 @@ uint32_t DeclResultIdMapper::createCounterVar(const ValueDecl *decl) {
 std::vector<uint32_t> DeclResultIdMapper::collectStageVars() const {
   std::vector<uint32_t> vars;
 
+  for (auto var : glPerVertex.getStageInVars())
+    vars.push_back(var);
+  for (auto var : glPerVertex.getStageOutVars())
+    vars.push_back(var);
+
   for (const auto &var : stageVars)
     vars.push_back(var.getSpirvId());
 
@@ -648,188 +745,214 @@ bool DeclResultIdMapper::decorateResourceBindings() {
   return noError;
 }
 
-QualType
-DeclResultIdMapper::getFnParamOrRetType(const DeclaratorDecl *decl) const {
-  if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
-    return funcDecl->getReturnType();
+bool DeclResultIdMapper::createStageVars(
+    const DeclaratorDecl *decl, const hlsl::SigPoint *sigPoint, bool asInput,
+    QualType type, uint32_t arraySize, llvm::Optional<uint32_t> invocationId,
+    uint32_t *value, const llvm::Twine &namePrefix) {
+  // invocationId should only be used for handling HS per-vertex output.
+  if (invocationId.hasValue()) {
+    assert(shaderModel.IsHS() && arraySize != 0 && !asInput);
   }
-  return decl->getType();
-}
 
-uint32_t DeclResultIdMapper::createStageVarWithoutSemantics(
-    bool isInput, uint32_t typeId, const llvm::StringRef name,
-    const clang::VKLocationAttr *loc) {
-  const hlsl::SigPoint *sigPoint = hlsl::SigPoint::GetSigPoint(
-      hlsl::SigPointFromInputQual(isInput ? hlsl::DxilParamInputQual::In
-                                          : hlsl::DxilParamInputQual::Out,
-                                  shaderModel.GetKind(), /*isPC*/ false));
-  StageVar stageVar(sigPoint, name, nullptr, 0, typeId);
-  const llvm::Twine fullName = (isInput ? "in.var." : "out.var.") + name;
-  const spv::StorageClass sc =
-      isInput ? spv::StorageClass::Input : spv::StorageClass::Output;
-  const uint32_t varId = theBuilder.addStageIOVar(typeId, sc, fullName.str());
-  if (varId == 0)
-    return 0;
-  stageVar.setSpirvId(varId);
-  stageVar.setLocationAttr(loc);
-  stageVars.push_back(stageVar);
-  return varId;
-}
-
-bool DeclResultIdMapper::createStageVars(const DeclaratorDecl *decl,
-                                         uint32_t *value, bool asInput,
-                                         const llvm::Twine &namePrefix,
-                                         bool isPatchConstant,
-                                         bool isOutputStream) {
-  QualType type = getFnParamOrRetType(decl);
   if (type->isVoidType()) {
     // No stage variables will be created for void type.
     return true;
   }
 
   uint32_t typeId = typeTranslator.translateType(type);
+  llvm::StringRef semanticStr;
+  const hlsl::Semantic *semantic = {};
+  uint32_t semanticIndex = {};
 
-  llvm::StringRef semanticStr = getStageVarSemantic(decl);
-  if (!semanticStr.empty()) {
+  if (getStageVarSemantic(decl, &semanticStr, &semantic, &semanticIndex)) {
     // Found semantic attached directly to this Decl. This means we need to
     // map this decl to a single stage variable.
 
-    hlsl::DxilParamInputQual qual =
-        asInput ? hlsl::DxilParamInputQual::In : hlsl::DxilParamInputQual::Out;
-
-    // The inputs to the geometry shader that have a primitive type qualifier
-    // must use 'InputPrimitive'.
-    if (asInput && shaderModel.IsGS() && hasGSPrimitiveTypeQualifier(decl))
-      qual = hlsl::DxilParamInputQual::InputPrimitive;
-
-    // Note that geometry shaders have output streams that are required to be
-    // marked as "inout". The DxilParamInputQual for these cases must be
-    // 'OutStream' rather than 'Out'.
-    // TODO: Add support for multiple output streams.
-    if (!asInput && isOutputStream)
-      qual = hlsl::DxilParamInputQual::OutStream0;
-
-    const hlsl::SigPoint *sigPoint =
-        hlsl::SigPoint::GetSigPoint(hlsl::SigPointFromInputQual(
-            qual, shaderModel.GetKind(), isPatchConstant));
-
-    llvm::StringRef semanticName;
-    uint32_t semanticIndex = 0;
-    hlsl::Semantic::DecomposeNameAndIndex(semanticStr, &semanticName,
-                                          &semanticIndex);
-    const auto *semantic = hlsl::Semantic::GetByName(semanticName);
-
     // Error out when the given semantic is invalid in this shader model
     if (hlsl::SigPoint::GetInterpretation(
             semantic->GetKind(), sigPoint->GetKind(), shaderModel.GetMajor(),
             shaderModel.GetMinor()) ==
         hlsl::DXIL::SemanticInterpretationKind::NA) {
-      emitError("invalid semantic %0 for shader module %1")
+      emitError("invalid semantic %0 for shader model %1")
           << semanticStr << shaderModel.GetName();
       return false;
     }
 
-    // SV_DomainLocation refers to a float2 (u,v), whereas TessCoord is a
-    // float3 (u,v,w). To ensure SPIR-V validity, we must create a float3 and
-    // extract a float2 from it before passing it to the main function.
-    if (semantic->GetKind() == hlsl::DXIL::SemanticKind::DomainLocation) {
+    // Special handling of certain mapping between HLSL semantics and
+    // SPIR-V builtin:
+    // * SV_Position/SV_CullDistance/SV_ClipDistance should be grouped into the
+    //   gl_PerVertex struct in vertex processing stages.
+    // * SV_DomainLocation can refer to a float2, whereas TessCoord is a float3.
+    //   To ensure SPIR-V validity, we must create a float3 and  extract a
+    //   float2 from it before passing it to the main function.
+    if (glPerVertex.tryToAccess(semantic->GetKind(), semanticIndex,
+                                invocationId, value, sigPoint->GetKind()))
+      return true;
+
+    if (semantic->GetKind() == hlsl::Semantic::Kind::DomainLocation)
       typeId = theBuilder.getVecType(theBuilder.getFloat32Type(), 3);
-    }
+
+    // Handle the extra arrayness
+    const uint32_t elementTypeId = typeId;
+    if (arraySize != 0)
+      typeId = theBuilder.getArrayType(typeId,
+                                       theBuilder.getConstantUint32(arraySize));
 
     StageVar stageVar(sigPoint, semanticStr, semantic, semanticIndex, typeId);
     llvm::Twine name = namePrefix + "." + semanticStr;
     const uint32_t varId = createSpirvStageVar(&stageVar, name);
+
     if (varId == 0)
       return false;
 
+    stageVar.setSpirvId(varId);
+    stageVar.setLocationAttr(decl->getAttr<VKLocationAttr>());
+    stageVars.push_back(stageVar);
+
+    // TODO: the following may not be correct?
     if (sigPoint->GetSignatureKind() ==
         hlsl::DXIL::SignatureKind::PatchConstant)
       theBuilder.decorate(varId, spv::Decoration::Patch);
 
     // Decorate with interpolation modes for pixel shader input variables
-    if (shaderModel.IsPS() && sigPoint->IsInput()) {
-      const QualType elemType = typeTranslator.getElementType(type);
-
-      if (elemType->isBooleanType() || elemType->isIntegerType()) {
-        // TODO: Probably we can call hlsl::ValidateSignatureElement() for the
-        // following check.
-        if (decl->getAttr<HLSLLinearAttr>() ||
-            decl->getAttr<HLSLCentroidAttr>() ||
-            decl->getAttr<HLSLNoPerspectiveAttr>() ||
-            decl->getAttr<HLSLSampleAttr>()) {
-          emitError("only nointerpolation mode allowed for integer input "
-                    "parameters in pixel shader",
-                    decl->getLocation());
-        } else {
-          theBuilder.decorate(varId, spv::Decoration::Flat);
-        }
-      } else {
-        // Do nothing for HLSLLinearAttr since its the default
-        // Attributes can be used together. So cannot use else if.
-        if (decl->getAttr<HLSLCentroidAttr>())
-          theBuilder.decorate(varId, spv::Decoration::Centroid);
-        if (decl->getAttr<HLSLNoInterpolationAttr>())
-          theBuilder.decorate(varId, spv::Decoration::Flat);
-        if (decl->getAttr<HLSLNoPerspectiveAttr>())
-          theBuilder.decorate(varId, spv::Decoration::NoPerspective);
-        if (decl->getAttr<HLSLSampleAttr>()) {
-          theBuilder.requireCapability(spv::Capability::SampleRateShading);
-          theBuilder.decorate(varId, spv::Decoration::Sample);
-        }
-      }
-    }
-
-    stageVar.setSpirvId(varId);
-    stageVar.setLocationAttr(decl->getAttr<VKLocationAttr>());
-    stageVars.push_back(stageVar);
+    if (shaderModel.IsPS() && sigPoint->IsInput())
+      decoratePSInterpolationMode(decl, type, varId);
 
     if (asInput) {
       *value = theBuilder.createLoad(typeId, varId);
     } else {
-      theBuilder.createStore(varId, *value);
-    }
-  } else {
-    // If the decl itself doesn't have semantic, it should be a struct having
-    // all its fields with semantics. Or it should be an OutputStream
-    // parameterized by a structure type with all its fields with semantics.
-    if (hlsl::IsHLSLStreamOutputType(type)) {
-      isOutputStream = true;
-      type = hlsl::GetHLSLResourceResultType(type);
+      uint32_t ptr = varId;
+      if (invocationId.hasValue()) {
+        // Special handling of HS ouput, for which we write to only one element
+        // in the per-vertex data array: the one indexed by  SV_ControlPointID.
+        const uint32_t ptrType =
+            theBuilder.getPointerType(elementTypeId, spv::StorageClass::Output);
+        const uint32_t index = invocationId.getValue();
+        ptr = theBuilder.createAccessChain(ptrType, varId, index);
+      }
+      theBuilder.createStore(ptr, *value);
     }
-    assert(type->isStructureType() && "found non-struct decls without semantics");
 
-    const auto *structDecl = cast<RecordType>(type.getTypePtr())->getDecl();
+    return true;
+  }
 
-    if (asInput) {
-      // If this decl translates into multiple stage input variables, we need to
-      // load their values into a composite.
-      llvm::SmallVector<uint32_t, 4> subValues;
-      for (const auto *field : structDecl->fields()) {
-        uint32_t subValue = 0;
-        if (!createStageVars(field, &subValue, true, namePrefix,
-                             isPatchConstant, isOutputStream))
-          return false;
-        subValues.push_back(subValue);
-      }
+  // If the decl itself doesn't have semantic string attached, it should be
+  // a struct having all its fields with semantic strings.
+  if (!type->isStructureType()) {
+    emitError("semantic string missing for shader %select{output|input}0 "
+              "variable '%1'",
+              decl->getLocStart())
+        << asInput << decl->getName();
+    return false;
+  }
+
+  const auto *structDecl = cast<RecordType>(type.getTypePtr())->getDecl();
+
+  if (asInput) {
+    // If this decl translates into multiple stage input variables, we need to
+    // load their values into a composite.
+    llvm::SmallVector<uint32_t, 4> subValues;
+
+    for (const auto *field : structDecl->fields()) {
+      uint32_t subValue = 0;
+      if (!createStageVars(field, sigPoint, asInput, field->getType(),
+                           arraySize, invocationId, &subValue, namePrefix))
+        return false;
+      subValues.push_back(subValue);
+    }
+
+    if (arraySize == 0) {
       *value = theBuilder.createCompositeConstruct(typeId, subValues);
-    } else {
-      // If this decl translates into multiple stage output variables, we need
-      // to store the value components into them.
+      return true;
+    }
+
+    // Handle the extra level of arrayness.
+
+    // We need to return an array of structs. But we get arrays of fields
+    // from visiting all fields. So now we need to extract all the elements
+    // at the same index of each field arrays and compose a new struct out
+    // of them.
+    const uint32_t structType = typeTranslator.translateType(type);
+    const uint32_t arrayType = theBuilder.getArrayType(
+        structType, theBuilder.getConstantUint32(arraySize));
+    llvm::SmallVector<uint32_t, 16> arrayElements;
+
+    for (uint32_t arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex) {
+      llvm::SmallVector<uint32_t, 8> fields;
+
+      // Extract the element at index arrayIndex from each field
       for (const auto *field : structDecl->fields()) {
         const uint32_t fieldType =
             typeTranslator.translateType(field->getType());
-        uint32_t subValue = theBuilder.createCompositeExtract(
-            fieldType, *value, {field->getFieldIndex()});
-        if (!createStageVars(field, &subValue, false, namePrefix,
-                             isPatchConstant, isOutputStream))
-          return false;
+        fields.push_back(theBuilder.createCompositeExtract(
+            fieldType, subValues[field->getFieldIndex()], {arrayIndex}));
       }
+      // Compose a new struct out of them
+      arrayElements.push_back(
+          theBuilder.createCompositeConstruct(structType, fields));
+    }
+
+    *value = theBuilder.createCompositeConstruct(arrayType, arrayElements);
+  } else {
+    // Unlike reading, which may require us to read stand-alone builtins and
+    // stage input variables and compose an array of structs out of them,
+    // it happens that we don't need to write an array of structs in a bunch
+    // for all shader stages:
+    //
+    // * VS: output is a single struct, without extra arrayness
+    // * HS: output is an array of structs, with extra arrayness,
+    //       but we only write to the struct at the InvocationID index
+    // * DS: output is a single struct, without extra arrayness
+    // * GS: output is controlled by OpEmitVertex, one vertex per time
+    //
+    // The interesting shader stage is HS. We need the InvocationID to write
+    // out the value to the correct array element.
+    for (const auto *field : structDecl->fields()) {
+      const uint32_t fieldType = typeTranslator.translateType(field->getType());
+      uint32_t subValue = theBuilder.createCompositeExtract(
+          fieldType, *value, {field->getFieldIndex()});
+      if (!createStageVars(field, sigPoint, asInput, field->getType(),
+                           arraySize, invocationId, &subValue, namePrefix))
+        return false;
     }
   }
 
   return true;
 }
 
+void DeclResultIdMapper::decoratePSInterpolationMode(const DeclaratorDecl *decl,
+                                                     QualType type,
+                                                     uint32_t varId) {
+  const QualType elemType = typeTranslator.getElementType(type);
+
+  if (elemType->isBooleanType() || elemType->isIntegerType()) {
+    // TODO: Probably we can call hlsl::ValidateSignatureElement() for the
+    // following check.
+    if (decl->getAttr<HLSLLinearAttr>() || decl->getAttr<HLSLCentroidAttr>() ||
+        decl->getAttr<HLSLNoPerspectiveAttr>() ||
+        decl->getAttr<HLSLSampleAttr>()) {
+      emitError("only nointerpolation mode allowed for integer input "
+                "parameters in pixel shader",
+                decl->getLocation());
+    } else {
+      theBuilder.decorate(varId, spv::Decoration::Flat);
+    }
+  } else {
+    // Do nothing for HLSLLinearAttr since its the default
+    // Attributes can be used together. So cannot use else if.
+    if (decl->getAttr<HLSLCentroidAttr>())
+      theBuilder.decorate(varId, spv::Decoration::Centroid);
+    if (decl->getAttr<HLSLNoInterpolationAttr>())
+      theBuilder.decorate(varId, spv::Decoration::Flat);
+    if (decl->getAttr<HLSLNoPerspectiveAttr>())
+      theBuilder.decorate(varId, spv::Decoration::NoPerspective);
+    if (decl->getAttr<HLSLSampleAttr>()) {
+      theBuilder.requireCapability(spv::Capability::SampleRateShading);
+      theBuilder.decorate(varId, spv::Decoration::Sample);
+    }
+  }
+}
+
 uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
                                                  const llvm::Twine &name) {
   using spv::BuiltIn;
@@ -850,13 +973,20 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
   // According to DXIL spec, the Position SV can be used by all SigPoints
   // other than PCIn, HSIn, GSIn, PSOut, CSIn.
   // According to Vulkan spec, the Position BuiltIn can only be used
-  // by PSOut, HS/DS/GS In/Out.
+  // by VSOut, HS/DS/GS In/Out.
   case hlsl::Semantic::Kind::Position: {
     switch (sigPointKind) {
     case hlsl::SigPoint::Kind::VSIn:
+    case hlsl::SigPoint::Kind::PCOut:
+    case hlsl::SigPoint::Kind::DSIn:
       return theBuilder.addStageIOVar(type, sc, name.str());
     case hlsl::SigPoint::Kind::VSOut:
+    case hlsl::SigPoint::Kind::HSCPIn:
+    case hlsl::SigPoint::Kind::HSCPOut:
+    case hlsl::SigPoint::Kind::DSCPIn:
     case hlsl::SigPoint::Kind::DSOut:
+    case hlsl::SigPoint::Kind::GSVIn:
+      llvm_unreachable("should be handled in gl_PerVertex struct");
     case hlsl::SigPoint::Kind::GSOut:
       stageVar->setIsSpirvBuiltin();
       return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::Position);
@@ -864,9 +994,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
       stageVar->setIsSpirvBuiltin();
       return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::FragCoord);
     default:
-      emitError("semantic Position for SigPoint %0 unimplemented yet")
-          << sigPoint->GetName();
-      break;
+      llvm_unreachable("invalid usage of SV_Position sneaked in");
     }
   }
   // According to DXIL spec, the VertexID SV can only be used by VSIn.
@@ -906,6 +1034,32 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar,
                                   spv::ExecutionMode::DepthLess, {});
     return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::FragDepth);
   }
+  // According to DXIL spec, the ClipDistance/CullDistance SV can be used by all
+  // SigPoints other than PCIn, HSIn, GSIn, PSOut, CSIn.
+  // According to Vulkan spec, the ClipDistance/CullDistance BuiltIn can only
+  // be
+  // used by VSOut, HS/DS/GS In/Out.
+  case hlsl::Semantic::Kind::ClipDistance:
+  case hlsl::Semantic::Kind::CullDistance: {
+    switch (sigPointKind) {
+    case hlsl::SigPoint::Kind::VSIn:
+    case hlsl::SigPoint::Kind::PCOut:
+    case hlsl::SigPoint::Kind::DSIn:
+      return theBuilder.addStageIOVar(type, sc, name.str());
+    case hlsl::SigPoint::Kind::VSOut:
+    case hlsl::SigPoint::Kind::HSCPIn:
+    case hlsl::SigPoint::Kind::HSCPOut:
+    case hlsl::SigPoint::Kind::DSCPIn:
+    case hlsl::SigPoint::Kind::DSOut:
+    case hlsl::SigPoint::Kind::GSVIn:
+    case hlsl::SigPoint::Kind::GSOut:
+    case hlsl::SigPoint::Kind::PSIn:
+      llvm_unreachable("should be handled in gl_PerVertex struct");
+    default:
+      llvm_unreachable(
+          "invalid usage of SV_ClipDistance/SV_CullDistance sneaked in");
+    }
+  }
   // According to DXIL spec, the IsFrontFace SV can only be used by GSOut and
   // PSIn.
   // According to Vulkan spec, the FrontFacing BuitIn can only be used in PSIn.

+ 44 - 23
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -25,6 +25,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Twine.h"
 
+#include "GlPerVertex.h"
 #include "SpirvEvalInfo.h"
 #include "TypeTranslator.h"
 
@@ -133,7 +134,7 @@ private:
 /// is required because of the semantic differences between DirectX and
 /// Vulkan and the essence of HLSL as the front-end language for DirectX.
 /// A normal variable attached with some semantic will be translated into a
-/// single stage variables if it is of non-struct type. If it is of struct
+/// single stage variable if it is of non-struct type. If it is of struct
 /// type, the fields with attached semantics will need to be translated into
 /// stage variables per Vulkan's requirements.
 class DeclResultIdMapper {
@@ -147,8 +148,14 @@ public:
   /// true on success. SPIR-V instructions will also be generated to update the
   /// contents of the output variables by extracting sub-values from the given
   /// storedValue.
+  ///
+  /// Note that the control point stage output variable of HS should be created
+  /// by the other overload.
   bool createStageOutputVar(const DeclaratorDecl *decl, uint32_t storedValue,
                             bool isPatchConstant);
+  /// \brief Overload for handling HS control point stage ouput variable.
+  bool createStageOutputVar(const DeclaratorDecl *decl, uint32_t arraySize,
+                            uint32_t invocationId, uint32_t storedValue);
 
   /// \brief Creates the stage input variables by parsing the semantics attached
   /// to the given function's parameter and returns true on success. SPIR-V
@@ -157,16 +164,6 @@ public:
   bool createStageInputVar(const ParmVarDecl *paramDecl, uint32_t *loadedValue,
                            bool isPatchConstant);
 
-  /// \brief Creates an input/output stage variable which does not have any
-  /// semantics (such as InputPatch/OutputPatch in Hull shaders). This method
-  /// does not create a Load/Store from/to the created stage variable and leaves
-  /// it to the caller to do so as they see fit, because it is possible that the
-  /// stage variable may have to be accessed differently (using OpAccessChain
-  /// for example).
-  uint32_t createStageVarWithoutSemantics(bool isInput, uint32_t typeId,
-                                          const llvm::StringRef name,
-                                          const clang::VKLocationAttr *loc);
-
   /// \brief Creates a function-scope paramter in the current function and
   /// returns its <result-id>.
   uint32_t createFnParam(uint32_t paramType, const ParmVarDecl *param);
@@ -207,7 +204,7 @@ public:
   /// \brief Sets the <result-id> of the entry function.
   void setEntryFunctionId(uint32_t id) { entryFunctionId = id; }
 
-public:
+private:
   /// The struct containing SPIR-V information of a AST Decl.
   struct DeclSpirvInfo {
     DeclSpirvInfo(uint32_t result = 0,
@@ -234,6 +231,7 @@ public:
   /// Returns nullptr if no such decl was previously registered.
   const DeclSpirvInfo *getDeclSpirvInfo(const NamedDecl *decl) const;
 
+public:
   /// \brief Returns the information for the given decl.
   ///
   /// This method will panic if the given decl is not registered.
@@ -289,10 +287,6 @@ private:
   /// construction.
   bool finalizeStageIOLocations(bool forInput);
 
-  /// Returns the type of the given decl. If the given decl is a FunctionDecl,
-  /// returns its result type.
-  QualType getFnParamOrRetType(const DeclaratorDecl *decl) const;
-
   /// Creates a variable of struct type with explicit layout decorations.
   /// The sub-Decls in the given DeclContext will be treated as the struct
   /// fields. The struct type will be named as typeName, and the variable
@@ -303,13 +297,29 @@ private:
                                            llvm::StringRef typeName,
                                            llvm::StringRef varName);
 
-  /// Creates all the stage variables mapped from semantics on the given decl
-  /// and returns true on success.
+  /// Creates all the stage variables mapped from semantics on the given decl.
+  /// Returns true on sucess.
+  ///
+  /// If decl is of struct type, this means flattening it and create stand-
+  /// alone variables for each field. If arraySize is not zero, the created
+  /// stage variables will have an additional arrayness over its original type.
+  /// This is for supporting HS/DS/GS, which takes in primitives containing
+  /// multiple vertices. asType should be the type we are treating decl as;
+  /// For HS/DS/GS, the outermost arrayness should be discarded and use
+  /// arraySize instead.
+  ///
+  /// Also performs updating the stage variables (loading/storing from/to the
+  /// given value) depending on asInput.
+  ///
+  /// invocationId is only used for HS to indicate the index of the output
+  /// array element to write to.
   ///
   /// Assumes the decl has semantic attached to itself or to its fields.
-  bool createStageVars(const DeclaratorDecl *decl, uint32_t *value,
-                       bool asInput, const llvm::Twine &namePrefix,
-                       bool isPatchConstant, bool isOutputStream = false);
+  bool createStageVars(const DeclaratorDecl *decl,
+                       const hlsl::SigPoint *sigPoint, bool asInput,
+                       QualType type, uint32_t arraySize,
+                       llvm::Optional<uint32_t> invocationId, uint32_t *value,
+                       const llvm::Twine &namePrefix);
 
   /// Creates the SPIR-V variable instruction for the given StageVar and returns
   /// the <result-id>. Also sets whether the StageVar is a SPIR-V builtin and
@@ -321,6 +331,11 @@ private:
   /// structured buffer.
   uint32_t createCounterVar(const ValueDecl *decl);
 
+  /// Decorates varId of the given asType with proper interpolation modes
+  /// considering the attributes on the given decl.
+  void decoratePSInterpolationMode(const DeclaratorDecl *decl, QualType asType,
+                                   uint32_t varId);
+
   /// Returns the proper SPIR-V storage class (Input or Output) for the given
   /// SigPoint.
   spv::StorageClass getStorageClassForSigPoint(const hlsl::SigPoint *);
@@ -344,8 +359,13 @@ private:
   llvm::SmallVector<StageVar, 8> stageVars;
   /// Vector of all defined resource variables.
   llvm::SmallVector<ResourceVar, 8> resourceVars;
-  /// Mapping from {Append|Consume}StructuredBuffers to their counter variables
+  /// Mapping from {RW|Append|Consume}StructuredBuffers to their
+  /// counter variables
   llvm::DenseMap<const NamedDecl *, uint32_t> counterVars;
+
+public:
+  /// The gl_PerVertex structs for both input and output
+  GlPerVertex glPerVertex;
 };
 
 DeclResultIdMapper::DeclResultIdMapper(const hlsl::ShaderModel &model,
@@ -354,7 +374,8 @@ DeclResultIdMapper::DeclResultIdMapper(const hlsl::ShaderModel &model,
                                        DiagnosticsEngine &diag,
                                        const EmitSPIRVOptions &options)
     : shaderModel(model), theBuilder(builder), spirvOptions(options),
-      diags(diag), typeTranslator(context, builder, diag), entryFunctionId(0) {}
+      diags(diag), typeTranslator(context, builder, diag), entryFunctionId(0),
+      glPerVertex(model, context, builder, typeTranslator) {}
 
 bool DeclResultIdMapper::decorateStageIOLocations() {
   // Try both input and output even if input location assignment failed

+ 759 - 0
tools/clang/lib/SPIRV/GlPerVertex.cpp

@@ -0,0 +1,759 @@
+//===--- GlPerVertex.cpp - GlPerVertex implementation ------------*- C++ -*-==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GlPerVertex.h"
+
+#include <algorithm>
+
+#include "clang/AST/HlslTypes.h"
+
+namespace clang {
+namespace spirv {
+
+namespace {
+/// \brief Returns true if the given decl has a semantic string attached and
+/// writes the info to *semanticStr, *semantic, and *semanticIndex.
+// TODO: duplication! Same as the one in DeclResultIdMapper.cpp
+bool getStageVarSemantic(const NamedDecl *decl, llvm::StringRef *semanticStr,
+                         const hlsl::Semantic **semantic,
+                         uint32_t *semanticIndex) {
+  for (auto *annotation : decl->getUnusualAnnotations()) {
+    if (auto *sema = dyn_cast<hlsl::SemanticDecl>(annotation)) {
+      *semanticStr = sema->SemanticName;
+      llvm::StringRef semanticName;
+      hlsl::Semantic::DecomposeNameAndIndex(*semanticStr, &semanticName,
+                                            semanticIndex);
+      *semantic = hlsl::Semantic::GetByName(semanticName);
+      return true;
+    }
+  }
+  return false;
+}
+
+/// Returns the type of the given decl. If the given decl is a FunctionDecl,
+/// returns its result type.
+inline QualType getTypeOrFnRetType(const DeclaratorDecl *decl) {
+  if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
+    return funcDecl->getReturnType();
+  }
+  return decl->getType();
+}
+} // anonymous namespace
+
+GlPerVertex::GlPerVertex(const hlsl::ShaderModel &sm, ASTContext &context,
+                         ModuleBuilder &builder, TypeTranslator &translator)
+    : shaderModel(sm), astContext(context), theBuilder(builder),
+      typeTranslator(translator), inIsGrouped(true), outIsGrouped(true),
+      inBlockVar(0), outBlockVar(0), inClipVar(0), inCullVar(0), outClipVar(0),
+      outCullVar(0), inArraySize(0), outArraySize(0), inClipArraySize(1),
+      outClipArraySize(1), inCullArraySize(1), outCullArraySize(1) {}
+
+void GlPerVertex::generateVars(uint32_t inArrayLen, uint32_t outArrayLen) {
+  // Calling this method twice is an internal error.
+  assert(inBlockVar == 0);
+  assert(outBlockVar == 0);
+
+  inArraySize = inArrayLen;
+  outArraySize = outArrayLen;
+
+  switch (shaderModel.GetKind()) {
+  case hlsl::ShaderModel::Kind::Vertex:
+    outBlockVar = createBlockVar(/*asInput=*/false, 0);
+    break;
+  case hlsl::ShaderModel::Kind::Hull:
+    inBlockVar = createBlockVar(/*asInput=*/true, inArraySize);
+    outBlockVar = createBlockVar(/*asInput=*/false, outArraySize);
+    break;
+  case hlsl::ShaderModel::Kind::Domain:
+    inBlockVar = createBlockVar(/*asInput=*/true, inArraySize);
+    outBlockVar = createBlockVar(/*asInput=*/false, 0);
+    break;
+  case hlsl::ShaderModel::Kind::Geometry:
+    inBlockVar = createBlockVar(/*asInput=*/true, inArraySize);
+    if (!outClipType.empty())
+      outClipVar = createClipDistanceVar(/*asInput=*/false, outClipArraySize);
+    if (!outCullType.empty())
+      outCullVar = createCullDistanceVar(/*asInput=*/false, outCullArraySize);
+    outIsGrouped = false;
+    break;
+  case hlsl::ShaderModel::Kind::Pixel:
+    if (!inClipType.empty())
+      inClipVar = createClipDistanceVar(/*asInput=*/true, inClipArraySize);
+    if (!inCullType.empty())
+      inCullVar = createCullDistanceVar(/*asInput=*/true, inCullArraySize);
+    inIsGrouped = false;
+    break;
+  }
+}
+
+llvm::SmallVector<uint32_t, 4> GlPerVertex::getStageInVars() const {
+  llvm::SmallVector<uint32_t, 4> vars;
+  if (inIsGrouped) {
+    if (inBlockVar)
+      vars.push_back(inBlockVar);
+  } else {
+    if (inClipVar)
+      vars.push_back(inClipVar);
+    if (inCullVar)
+      vars.push_back(inCullVar);
+  }
+
+  return vars;
+}
+
+llvm::SmallVector<uint32_t, 4> GlPerVertex::getStageOutVars() const {
+  llvm::SmallVector<uint32_t, 4> vars;
+  if (outIsGrouped) {
+    if (outBlockVar)
+      vars.push_back(outBlockVar);
+  } else {
+    if (outClipVar)
+      vars.push_back(outClipVar);
+    if (outCullVar)
+      vars.push_back(outCullVar);
+  }
+
+  return vars;
+}
+
+bool GlPerVertex::recordClipCullDistanceDecl(const DeclaratorDecl *decl,
+                                             bool asInput) {
+  const QualType type = getTypeOrFnRetType(decl);
+
+  if (type->isVoidType())
+    return true;
+
+  return doClipCullDistanceDecl(decl, type, asInput);
+}
+
+bool GlPerVertex::doClipCullDistanceDecl(const DeclaratorDecl *decl,
+                                         QualType baseType, bool asInput) {
+
+  llvm::StringRef semanticStr;
+  const hlsl::Semantic *semantic = {};
+  uint32_t semanticIndex = {};
+
+  if (!getStageVarSemantic(decl, &semanticStr, &semantic, &semanticIndex)) {
+    if (baseType->isStructureType()) {
+      const auto *structDecl =
+          cast<RecordType>(baseType.getTypePtr())->getDecl();
+      // Go through each field to see if there is any usage of
+      // SV_ClipDistance/SV_CullDistance.
+      for (const auto *field : structDecl->fields()) {
+        if (!doClipCullDistanceDecl(field, field->getType(), asInput))
+          return false;
+      }
+      return true;
+    }
+
+    // For these HS/DS/GS specific data types, semantic strings are attached
+    // to the underlying struct's fields.
+    if (hlsl::IsHLSLInputPatchType(baseType)) {
+      return doClipCullDistanceDecl(
+          decl, hlsl::GetHLSLInputPatchElementType(baseType), asInput);
+    }
+    if (hlsl::IsHLSLOutputPatchType(baseType)) {
+      return doClipCullDistanceDecl(
+          decl, hlsl::GetHLSLOutputPatchElementType(baseType), asInput);
+    }
+    if (hlsl::IsHLSLStreamOutputType(baseType)) {
+      return doClipCullDistanceDecl(
+          decl, hlsl::GetHLSLOutputPatchElementType(baseType), asInput);
+    }
+
+    emitError("semantic string missing for shader %select{output|input}0 "
+              "variable '%1'",
+              decl->getLocStart())
+        << asInput << decl->getName();
+    return false;
+  }
+
+  // Semantic string is attched to this decl directly
+
+  // Select the corresponding data member to update
+  SemanticIndexToTypeMap *typeMap = nullptr;
+  uint32_t *blockArraySize = asInput ? &inArraySize : &outArraySize;
+  bool isCull = false;
+
+  switch (semantic->GetKind()) {
+  case hlsl::Semantic::Kind::ClipDistance:
+    typeMap = asInput ? &inClipType : &outClipType;
+    break;
+  case hlsl::Semantic::Kind::CullDistance:
+    typeMap = asInput ? &inCullType : &outCullType;
+    isCull = true;
+    break;
+  default:
+    // Annotated with something other than SV_ClipDistance or SV_CullDistance.
+    // We don't care about such cases.
+    return true;
+  }
+
+  // Parameters marked as inout has reference type.
+  if (baseType->isReferenceType())
+    baseType = baseType->getPointeeType();
+
+  if (baseType->isFloatingType() || hlsl::IsHLSLVecType(baseType)) {
+    (*typeMap)[semanticIndex] = baseType;
+    return true;
+  }
+
+  if (baseType->isConstantArrayType()) {
+    if (shaderModel.IsHS() || shaderModel.IsDS() || shaderModel.IsGS()) {
+      // Ignore the outermost arrayness and check the inner type to be
+      // (vector of) floats
+
+      const auto *arrayType = astContext.getAsConstantArrayType(baseType);
+
+      // TODO: handle extra large array size?
+      if (*blockArraySize !=
+          static_cast<uint32_t>(arrayType->getSize().getZExtValue())) {
+        emitError("inconsistent array size for shader %select{output|input}0 "
+                  "variable '%1'",
+                  decl->getLocStart())
+            << asInput << decl->getName();
+        return false;
+      }
+
+      const QualType elemType = arrayType->getElementType();
+
+      if (elemType->isFloatingType() || hlsl::IsHLSLVecType(elemType)) {
+        (*typeMap)[semanticIndex] = elemType;
+        return true;
+      }
+
+      emitError("elements for %select{SV_ClipDistance|SV_CullDistance}0 "
+                "variable '%1' must be (vector of) floats",
+                decl->getLocStart())
+          << isCull << decl->getName();
+      return false;
+    }
+
+    emitError("%select{SV_ClipDistance|SV_CullDistance}0 variable '%1' not "
+              "allowed to be of array type",
+              decl->getLocStart())
+        << isCull << decl->getName();
+    return false;
+  }
+
+  emitError("incorrect type for %select{SV_ClipDistance|SV_CullDistance}0 "
+            "variable '%1'",
+            decl->getLocStart())
+      << isCull << decl->getName();
+  return false;
+}
+
+void GlPerVertex::calculateClipCullDistanceArraySize() {
+  // Updates the offset map and array size for the given input/output
+  // SV_ClipDistance/SV_CullDistance.
+  const auto updateSizeAndOffset = [](const SemanticIndexToTypeMap &typeMap,
+                                      SemanticIndexToArrayOffsetMap *offsetMap,
+                                      uint32_t *totalSize) {
+    // If no usage of SV_ClipDistance/SV_CullDistance was recorded,just
+    // return. This will keep the size defaulted to 1.
+    if (typeMap.empty())
+      return;
+
+    *totalSize = 0;
+
+    // Collect all indices and sort them
+    llvm::SmallVector<uint32_t, 8> indices;
+    for (const auto &kv : typeMap)
+      indices.push_back(kv.first);
+    std::sort(indices.begin(), indices.end(), std::less<uint32_t>());
+
+    for (uint32_t index : indices) {
+      const auto type = typeMap.find(index)->second;
+      QualType elemType = {};
+      uint32_t count = 0;
+
+      if (TypeTranslator::isScalarType(type)) {
+        (*offsetMap)[index] = (*totalSize)++;
+      } else if (TypeTranslator::isVectorType(type, &elemType, &count)) {
+        (*offsetMap)[index] = *totalSize;
+        *totalSize += count;
+      } else {
+        llvm_unreachable("SV_ClipDistance/SV_CullDistance not float or "
+                         "vector of float case sneaked in");
+      }
+    }
+  };
+
+  updateSizeAndOffset(inClipType, &inClipOffset, &inClipArraySize);
+  updateSizeAndOffset(inCullType, &inCullOffset, &inCullArraySize);
+  updateSizeAndOffset(outClipType, &outClipOffset, &outClipArraySize);
+  updateSizeAndOffset(outCullType, &outCullOffset, &outCullArraySize);
+}
+
+uint32_t GlPerVertex::createBlockVar(bool asInput, uint32_t arraySize) {
+  const llvm::StringRef typeName = "type.gl_PerVertex";
+  spv::StorageClass sc = spv::StorageClass::Input;
+  llvm::StringRef varName = "gl_PerVertexIn";
+  uint32_t clipSize = inClipArraySize;
+  uint32_t cullSize = inCullArraySize;
+
+  if (!asInput) {
+    sc = spv::StorageClass::Output;
+    varName = "gl_PerVertexOut";
+    clipSize = outClipArraySize;
+    cullSize = outCullArraySize;
+  }
+
+  uint32_t typeId =
+      typeTranslator.getGlPerVertexStruct(clipSize, cullSize, typeName);
+
+  // Handle the extra arrayness over the block
+  if (arraySize != 0) {
+    const uint32_t arraySizeId = theBuilder.getConstantUint32(arraySize);
+    typeId = theBuilder.getArrayType(typeId, arraySizeId);
+  }
+
+  return theBuilder.addStageIOVar(typeId, sc, varName);
+}
+
+uint32_t GlPerVertex::createPositionVar(bool asInput) {
+  const uint32_t type = theBuilder.getVecType(theBuilder.getFloat32Type(), 4);
+  const spv::StorageClass sc =
+      asInput ? spv::StorageClass::Input : spv::StorageClass::Output;
+  // Special handling here. Requesting Position for input means we are in
+  // PS, which should use FragCoord instead of Position.
+  assert(asInput ? shaderModel.IsPS() : true);
+  const spv::BuiltIn builtin =
+      asInput ? spv::BuiltIn::FragCoord : spv::BuiltIn::Position;
+
+  return theBuilder.addStageBuiltinVar(type, sc, builtin);
+}
+
+uint32_t GlPerVertex::createClipDistanceVar(bool asInput, uint32_t arraySize) {
+  const uint32_t type = theBuilder.getArrayType(
+      theBuilder.getFloat32Type(), theBuilder.getConstantUint32(arraySize));
+  spv::StorageClass sc =
+      asInput ? spv::StorageClass::Input : spv::StorageClass::Output;
+
+  return theBuilder.addStageBuiltinVar(type, sc, spv::BuiltIn::ClipDistance);
+}
+
+uint32_t GlPerVertex::createCullDistanceVar(bool asInput, uint32_t arraySize) {
+  const uint32_t type = theBuilder.getArrayType(
+      theBuilder.getFloat32Type(), theBuilder.getConstantUint32(arraySize));
+  spv::StorageClass sc =
+      asInput ? spv::StorageClass::Input : spv::StorageClass::Output;
+
+  return theBuilder.addStageBuiltinVar(type, sc, spv::BuiltIn::CullDistance);
+}
+
+bool GlPerVertex::tryToAccess(hlsl::Semantic::Kind semanticKind,
+                              uint32_t semanticIndex,
+                              llvm::Optional<uint32_t> invocationId,
+                              uint32_t *value,
+                              hlsl::SigPoint::Kind sigPointKind) {
+  // invocationId should only be used for HSPCOut.
+  assert(invocationId.hasValue() ? sigPointKind == hlsl::SigPoint::Kind::HSCPOut
+                                 : true);
+
+  switch (sigPointKind) {
+  case hlsl::SigPoint::Kind::HSCPIn:
+  case hlsl::SigPoint::Kind::DSCPIn:
+  case hlsl::SigPoint::Kind::GSVIn:
+    return readField(semanticKind, semanticIndex, value);
+  case hlsl::SigPoint::Kind::PSIn:
+    // We don't handle stand-alone Position builtin in this class.
+    return semanticKind == hlsl::Semantic::Kind::Position
+               ? 0 // Fall back to the normal path
+               : readField(semanticKind, semanticIndex, value);
+  case hlsl::SigPoint::Kind::VSOut:
+  case hlsl::SigPoint::Kind::HSCPOut:
+  case hlsl::SigPoint::Kind::DSOut:
+    return writeField(semanticKind, semanticIndex, invocationId, value);
+  case hlsl::SigPoint::Kind::GSOut:
+    // We don't handle stand-alone Position builtin in this class.
+    return semanticKind == hlsl::Semantic::Kind::Position
+               ? 0 // Fall back to the normal path
+               : writeField(semanticKind, semanticIndex, invocationId, value);
+  }
+
+  return false;
+}
+
+uint32_t GlPerVertex::readPosition() const {
+  assert(inIsGrouped); // We do not handle stand-alone Position builtin here.
+
+  // The Position builtin is always of float4 type.
+  const uint32_t fieldType =
+      theBuilder.getVecType(theBuilder.getFloat32Type(), 4);
+  const uint32_t ptrType =
+      theBuilder.getPointerType(fieldType, spv::StorageClass::Input);
+  const uint32_t fieldIndex = theBuilder.getConstantUint32(0);
+
+  if (inArraySize == 0) {
+    // The input builtin block is a single block. Only need one index to
+    // locate the Position builtin.
+    const uint32_t ptr =
+        theBuilder.createAccessChain(ptrType, inBlockVar, {fieldIndex});
+    return theBuilder.createLoad(fieldType, ptr);
+  }
+
+  // The input builtin block is an array of blocks, which means we need to
+  // read an array of float4 from an array of structs.
+
+  llvm::SmallVector<uint32_t, 8> elements;
+  for (uint32_t i = 0; i < inArraySize; ++i) {
+    const uint32_t arrayIndex = theBuilder.getConstantUint32(i);
+    // Get pointer into the array of structs. We need two indices to locate
+    // the Position builtin now: the first one is the array index, and the
+    // second one is the struct index.
+    const uint32_t ptr = theBuilder.createAccessChain(ptrType, inBlockVar,
+                                                      {arrayIndex, fieldIndex});
+    elements.push_back(theBuilder.createLoad(fieldType, ptr));
+  }
+  // Construct a new array of float4 for the Position builtins
+  const uint32_t arrayType = theBuilder.getArrayType(
+      fieldType, theBuilder.getConstantUint32(inArraySize));
+  return theBuilder.createCompositeConstruct(arrayType, elements);
+}
+
+uint32_t GlPerVertex::readClipCullArrayAsType(bool isClip, uint32_t offset,
+                                              QualType asType) const {
+  const uint32_t clipCullIndex = isClip ? 2 : 3;
+
+  // The ClipDistance/CullDistance is always an float array. We are accessing
+  // it using pointers, which should be of pointer to float type.
+  const uint32_t f32Type = theBuilder.getFloat32Type();
+  const uint32_t ptrType =
+      theBuilder.getPointerType(f32Type, spv::StorageClass::Input);
+
+  if (inArraySize == 0) {
+    // The input builtin block is a single block. Only need two indices to
+    // locate the array segment for this SV_ClipDistance/SV_CullDistance
+    // variable: one is the index in the gl_PerVertex struct, the other is
+    // the start offset within the float array.
+    QualType elemType = {};
+    uint32_t count = {};
+
+    if (TypeTranslator::isScalarType(asType)) {
+      const uint32_t offsetId = theBuilder.getConstantUint32(offset);
+      uint32_t ptr = 0;
+
+      if (inIsGrouped) {
+        ptr = theBuilder.createAccessChain(
+            ptrType, inBlockVar,
+            {theBuilder.getConstantUint32(clipCullIndex), offsetId});
+      } else {
+        ptr = theBuilder.createAccessChain(
+            ptrType, clipCullIndex == 2 ? inClipVar : inCullVar, {offsetId});
+      }
+      return theBuilder.createLoad(f32Type, ptr);
+    }
+
+    if (TypeTranslator::isVectorType(asType, &elemType, &count)) {
+      // The target SV_ClipDistance/SV_CullDistance variable is of vector
+      // type, then we need to construct a vector out of float array elements.
+      llvm::SmallVector<uint32_t, 4> elements;
+      for (uint32_t i = 0; i < count; ++i) {
+        // Read elements sequentially from the float array
+        const uint32_t offsetId = theBuilder.getConstantUint32(offset + i);
+        uint32_t ptr = 0;
+
+        if (inIsGrouped) {
+          ptr = theBuilder.createAccessChain(
+              ptrType, inBlockVar,
+              {theBuilder.getConstantUint32(clipCullIndex), offsetId});
+        } else {
+          ptr = theBuilder.createAccessChain(
+              ptrType, clipCullIndex == 2 ? inClipVar : inCullVar, {offsetId});
+        }
+        elements.push_back(theBuilder.createLoad(f32Type, ptr));
+      }
+      return theBuilder.createCompositeConstruct(
+          theBuilder.getVecType(f32Type, count), elements);
+    }
+
+    llvm_unreachable("SV_ClipDistance/SV_CullDistance not float or vector of "
+                     "float case sneaked in");
+  }
+
+  // The input builtin block is an array of block, which means we need to
+  // return an array of ClipDistance/CullDistance values from an array of
+  // struct. For this case, we need three indices to locate the element to
+  // read: the first one for indexing into the block array, the second one
+  // for indexing into the gl_PerVertex struct, and the third one for reading
+  // the correct element in the float array for ClipDistance/CullDistance.
+
+  assert(inIsGrouped); // Separated builtins won't have the extra arrayness.
+
+  llvm::SmallVector<uint32_t, 8> arrayElements;
+  QualType elemType = {};
+  uint32_t count = {};
+  uint32_t arrayType = {};
+  uint32_t arraySize = theBuilder.getConstantUint32(inArraySize);
+
+  if (TypeTranslator::isScalarType(asType)) {
+    arrayType = theBuilder.getArrayType(f32Type, arraySize);
+    for (uint32_t i = 0; i < inArraySize; ++i) {
+      const uint32_t ptr = theBuilder.createAccessChain(
+          ptrType, inBlockVar,
+          {theBuilder.getConstantUint32(i), // Block array index
+           theBuilder.getConstantUint32(clipCullIndex),
+           theBuilder.getConstantUint32(offset)});
+      arrayElements.push_back(theBuilder.createLoad(f32Type, ptr));
+    }
+  } else if (TypeTranslator::isVectorType(asType, &elemType, &count)) {
+    arrayType = theBuilder.getArrayType(theBuilder.getVecType(f32Type, count),
+                                        arraySize);
+    for (uint32_t i = 0; i < inArraySize; ++i) {
+      // For each gl_PerVertex block, we need to read a vector from it.
+      llvm::SmallVector<uint32_t, 4> vecElements;
+      for (uint32_t j = 0; j < count; ++j) {
+        const uint32_t ptr = theBuilder.createAccessChain(
+            ptrType, inBlockVar,
+            {theBuilder.getConstantUint32(i), // Block array index
+             theBuilder.getConstantUint32(clipCullIndex),
+             // Read elements sequentially from the float array
+             theBuilder.getConstantUint32(offset + j)});
+        vecElements.push_back(theBuilder.createLoad(f32Type, ptr));
+      }
+      arrayElements.push_back(theBuilder.createCompositeConstruct(
+          theBuilder.getVecType(f32Type, count), vecElements));
+    }
+  } else {
+    llvm_unreachable("SV_ClipDistance/SV_CullDistance not float or vector of "
+                     "float case sneaked in");
+  }
+
+  return theBuilder.createCompositeConstruct(arrayType, arrayElements);
+};
+
+bool GlPerVertex::readField(hlsl::Semantic::Kind semanticKind,
+                            uint32_t semanticIndex, uint32_t *value) {
+  switch (semanticKind) {
+  case hlsl::Semantic::Kind::Position:
+    *value = readPosition();
+    return true;
+  case hlsl::Semantic::Kind::ClipDistance: {
+    const auto offsetIter = inClipOffset.find(semanticIndex);
+    const auto typeIter = inClipType.find(semanticIndex);
+    // We should have recorded all these semantics before.
+    assert(offsetIter != inClipOffset.end());
+    assert(typeIter != inClipType.end());
+    *value = readClipCullArrayAsType(/*isClip=*/true, offsetIter->second,
+                                     typeIter->second);
+    return true;
+  }
+  case hlsl::Semantic::Kind::CullDistance: {
+    const auto offsetIter = inCullOffset.find(semanticIndex);
+    const auto typeIter = inCullType.find(semanticIndex);
+    // We should have recorded all these semantics before.
+    assert(offsetIter != inCullOffset.end());
+    assert(typeIter != inCullType.end());
+    *value = readClipCullArrayAsType(/*isClip=*/false, offsetIter->second,
+                                     typeIter->second);
+    return true;
+  }
+  }
+  return false;
+}
+
+void GlPerVertex::writePosition(llvm::Optional<uint32_t> invocationId,
+                                uint32_t value) const {
+  assert(outIsGrouped); // We do not handle stand-alone Position builtin here.
+
+  // The Position builtin is always of float4 type.
+  const uint32_t fieldType =
+      theBuilder.getVecType(theBuilder.getFloat32Type(), 4);
+  const uint32_t ptrType =
+      theBuilder.getPointerType(fieldType, spv::StorageClass::Output);
+  const uint32_t fieldIndex = theBuilder.getConstantUint32(0);
+
+  if (outArraySize == 0) {
+    // The input builtin block is a single block. Only need one index to
+    // locate the Position builtin.
+    const uint32_t ptr =
+        theBuilder.createAccessChain(ptrType, outBlockVar, {fieldIndex});
+    theBuilder.createStore(ptr, value);
+    return;
+  }
+
+  // Writing to an array only happens in HSCPOut.
+  assert(shaderModel.IsHS());
+  // And we are only writing to the array element with InvocationId as index.
+  assert(invocationId.hasValue());
+
+  // The input builtin block is an array of blocks, which means we need to
+  // to write a float4 to each gl_PerVertex in the array.
+
+  const uint32_t arrayIndex = invocationId.getValue();
+  // Get pointer into the array of structs. We need two indices to locate
+  // the Position builtin now: the first one is the array index, and the
+  // second one is the struct index.
+  const uint32_t ptr = theBuilder.createAccessChain(ptrType, outBlockVar,
+                                                    {arrayIndex, fieldIndex});
+  theBuilder.createStore(ptr, value);
+}
+
+void GlPerVertex::writeClipCullArrayFromType(
+    llvm::Optional<uint32_t> invocationId, bool isClip, uint32_t offset,
+    QualType fromType, uint32_t fromValue) const {
+  const uint32_t clipCullIndex = isClip ? 2 : 3;
+
+  // The ClipDistance/CullDistance is always an float array. We are accessing
+  // it using pointers, which should be of pointer to float type.
+  const uint32_t f32Type = theBuilder.getFloat32Type();
+  const uint32_t ptrType =
+      theBuilder.getPointerType(f32Type, spv::StorageClass::Output);
+
+  if (outArraySize == 0) {
+    // The input builtin block is a single block. Only need two indices to
+    // locate the array segment for this SV_ClipDistance/SV_CullDistance
+    // variable: one is the index in the gl_PerVertex struct, the other is
+    // the start offset within the float array.
+    QualType elemType = {};
+    uint32_t count = {};
+
+    if (TypeTranslator::isScalarType(fromType)) {
+      const uint32_t offsetId = theBuilder.getConstantUint32(offset);
+      uint32_t ptr = 0;
+
+      if (outIsGrouped) {
+        ptr = theBuilder.createAccessChain(
+            ptrType, outBlockVar,
+            {theBuilder.getConstantUint32(clipCullIndex), offsetId});
+      } else {
+        ptr = theBuilder.createAccessChain(
+            ptrType, clipCullIndex == 2 ? outClipVar : outCullVar, {offsetId});
+      }
+      theBuilder.createStore(ptr, fromValue);
+      return;
+    }
+
+    if (TypeTranslator::isVectorType(fromType, &elemType, &count)) {
+      // The target SV_ClipDistance/SV_CullDistance variable is of vector
+      // type. We need to write each component in the vector out.
+      for (uint32_t i = 0; i < count; ++i) {
+        // Write elements sequentially into the float array
+        const uint32_t offsetId = theBuilder.getConstantUint32(offset + i);
+        uint32_t ptr = 0;
+
+        if (outIsGrouped) {
+          ptr = theBuilder.createAccessChain(
+              ptrType, outBlockVar,
+              {theBuilder.getConstantUint32(clipCullIndex), offsetId});
+        } else {
+          ptr = theBuilder.createAccessChain(
+              ptrType, clipCullIndex == 2 ? outClipVar : outCullVar,
+              {offsetId});
+        }
+        const uint32_t subValue =
+            theBuilder.createCompositeExtract(f32Type, fromValue, {i});
+        theBuilder.createStore(ptr, subValue);
+      }
+      return;
+    }
+
+    llvm_unreachable("SV_ClipDistance/SV_CullDistance not float or vector of "
+                     "float case sneaked in");
+    return;
+  }
+
+  assert(outIsGrouped); // Separated builtins won't have the extra arrayness.
+
+  // Writing to an array only happens in HSCPOut.
+  assert(shaderModel.IsHS());
+  // And we are only writing to the array element with InvocationId as index.
+  assert(invocationId.hasValue());
+
+  // The output builtin block is an array of block, which means we need to
+  // write an array of ClipDistance/CullDistance values into an array of
+  // struct. For this case, we need three indices to locate the position to
+  // write: the first one for indexing into the block array, the second one
+  // for indexing into the gl_PerVertex struct, and the third one for the
+  // correct element in the float array for ClipDistance/CullDistance.
+
+  uint32_t arrayIndex = invocationId.getValue();
+  QualType elemType = {};
+  uint32_t count = {};
+
+  if (TypeTranslator::isScalarType(fromType)) {
+    const uint32_t ptr = theBuilder.createAccessChain(
+        ptrType, outBlockVar,
+        {arrayIndex, // Block array index
+         theBuilder.getConstantUint32(clipCullIndex),
+         theBuilder.getConstantUint32(offset)});
+    theBuilder.createStore(ptr, fromValue);
+    return;
+  }
+
+  if (TypeTranslator::isVectorType(fromType, &elemType, &count)) {
+    // For each gl_PerVertex block, we need to write a vector into it.
+    for (uint32_t i = 0; i < count; ++i) {
+      const uint32_t ptr = theBuilder.createAccessChain(
+          ptrType, outBlockVar,
+          {arrayIndex, // Block array index
+           theBuilder.getConstantUint32(clipCullIndex),
+           // Write elements sequentially into the float array
+           theBuilder.getConstantUint32(offset + i)});
+      const uint32_t subValue =
+          theBuilder.createCompositeExtract(f32Type, fromValue, {i});
+      theBuilder.createStore(ptr, subValue);
+    }
+    return;
+  }
+
+  llvm_unreachable("SV_ClipDistance/SV_CullDistance not float or vector of "
+                   "float case sneaked in");
+};
+
+bool GlPerVertex::writeField(hlsl::Semantic::Kind semanticKind,
+                             uint32_t semanticIndex,
+                             llvm::Optional<uint32_t> invocationId,
+                             uint32_t *value) {
+  // Similar to the writing logic in DeclResultIdMapper::createStageVars():
+  //
+  // Unlike reading, which may require us to read stand-alone builtins and
+  // stage input variables and compose an array of structs out of them,
+  // it happens that we don't need to write an array of structs in a bunch
+  // for all shader stages:
+  //
+  // * VS: output is a single struct, without extra arrayness
+  // * HS: output is an array of structs, with extra arrayness,
+  //       but we only write to the struct at the InvocationID index
+  // * DS: output is a single struct, without extra arrayness
+  // * GS: output is controlled by OpEmitVertex, one vertex per time
+  //
+  // The interesting shader stage is HS. We need the InvocationID to write
+  // out the value to the correct array element.
+  switch (semanticKind) {
+  case hlsl::Semantic::Kind::Position:
+    writePosition(invocationId, *value);
+    return true;
+  case hlsl::Semantic::Kind::ClipDistance: {
+    const auto offsetIter = outClipOffset.find(semanticIndex);
+    const auto typeIter = outClipType.find(semanticIndex);
+    // We should have recorded all these semantics before.
+    assert(offsetIter != outClipOffset.end());
+    assert(typeIter != outClipType.end());
+    writeClipCullArrayFromType(invocationId, /*isClip=*/true,
+                               offsetIter->second, typeIter->second, *value);
+    return true;
+  }
+  case hlsl::Semantic::Kind::CullDistance: {
+    const auto offsetIter = outCullOffset.find(semanticIndex);
+    const auto typeIter = outCullType.find(semanticIndex);
+    // We should have recorded all these semantics before.
+    assert(offsetIter != outCullOffset.end());
+    assert(typeIter != outCullType.end());
+    writeClipCullArrayFromType(invocationId, /*isClip=*/false,
+                               offsetIter->second, typeIter->second, *value);
+    return true;
+  }
+  }
+  return false;
+}
+
+} // end namespace spirv
+} // end namespace clang

+ 203 - 0
tools/clang/lib/SPIRV/GlPerVertex.h

@@ -0,0 +1,203 @@
+//===--- GlPerVertex.h - For handling gl_PerVertex members -------*- C++ -*-==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_SPIRV_GLPERVERTEX_H
+#define LLVM_CLANG_LIB_SPIRV_GLPERVERTEX_H
+
+#include "dxc/HLSL/DxilSemantic.h"
+#include "dxc/HLSL/DxilShaderModel.h"
+#include "dxc/HLSL/DxilSigPoint.h"
+#include "clang/SPIRV/ModuleBuilder.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include "TypeTranslator.h"
+
+namespace clang {
+namespace spirv {
+
+/// The class for representing special gl_PerVertex builtin interface block.
+/// The Position, PointSize, ClipDistance, and CullDistance builtin should
+/// be handled by this class, except for Position builtin used in GS output
+/// and PS input.
+///
+/// Although the Vulkan spec does not require this directly, it seems the only
+/// way to avoid violating the spec is to group the Position, ClipDistance, and
+/// CullDistance builtins together into a struct. That's also how GLSL handles
+/// these builtins. In GLSL, this struct is called gl_PerVertex.
+///
+/// This struct should appear as the entry point parameters but it should not
+/// have location assignment. We can have two such blocks at most: one for
+/// input, one for output.
+///
+/// Reading/writing of the ClipDistance/CullDistance builtin is not as
+/// straightforward as other builtins. This is because in HLSL, we can have
+/// multiple entities annotated with SV_ClipDistance/SV_CullDistance and they
+/// can be float or vector of float type. For example,
+///
+///   float2 var1 : SV_ClipDistance2,
+///   float  var2 : SV_ClipDistance0,
+///   float3 var3 : SV_ClipDistance1,
+///
+/// But in Vulkan, ClipDistance/CullDistance is required to be a float array.
+/// So we need to combine these variables into one single float array. The way
+/// we do it is by sorting all entities according to the SV_ClipDistance/
+/// SV_CullDistance index, and concatenating them tightly. So for the above,
+/// var2 will take the first two floats in the array, var3 will take the next
+/// three, and var1 will take the next two. In total, we have an size-6 float
+/// array for ClipDistance builtin.
+class GlPerVertex {
+public:
+  GlPerVertex(const hlsl::ShaderModel &sm, ASTContext &context,
+              ModuleBuilder &builder, TypeTranslator &translator);
+
+  /// Records a declaration of SV_ClipDistance/SV_CullDistance so later
+  /// we can caculate the ClipDistance/CullDistance array layout.
+  bool recordClipCullDistanceDecl(const DeclaratorDecl *decl, bool asInput);
+
+  /// Calculates the layout for ClipDistance/CullDistance arrays.
+  void calculateClipCullDistanceArraySize();
+
+  /// Emits SPIR-V code for the input and/or ouput gl_PerVertex builtin
+  /// interface blocks. If inputArrayLength is not zero, the input gl_PerVertex
+  /// will have an additional arrayness of the given size. Similarly for
+  /// outputArrayLength.
+  ///
+  /// Note that this method should be called after recordClipCullDistanceDecl()
+  /// and calculateClipCullDistanceArraySize().
+  void generateVars(uint32_t inputArrayLength, uint32_t outputArrayLength);
+
+  /// Returns the <result-id>s for stage input variables.
+  llvm::SmallVector<uint32_t, 4> getStageInVars() const;
+  /// Returns the <result-id>s for stage output variables.
+  llvm::SmallVector<uint32_t, 4> getStageOutVars() const;
+
+  /// Tries to access the builtin translated from the given HLSL semantic of the
+  /// given index. If sigPoint indicates this is input, builtins will be read
+  /// to compose a new temporary value of the correct type and writes to *value.
+  /// Otherwise, the *value will be decomposed and writes to the builtins.
+  /// Emits SPIR-V instructions and returns true if we are accessing builtins
+  /// belonging to gl_PerVertex. Does nothing and returns true if we are
+  /// accessing builtins not in gl_PerVertex. Returns false if errors occurs.
+  bool tryToAccess(hlsl::Semantic::Kind, uint32_t semanticIndex,
+                   llvm::Optional<uint32_t> invocationId, uint32_t *value,
+                   hlsl::SigPoint::Kind sigPoint);
+
+private:
+  template <unsigned N>
+  DiagnosticBuilder emitError(const char (&message)[N],
+                              SourceLocation loc = {}) {
+    const auto diagId = astContext.getDiagnostics().getCustomDiagID(
+        clang::DiagnosticsEngine::Error, message);
+    return astContext.getDiagnostics().Report(loc, diagId);
+  }
+
+  /// Creates a gl_PerVertex interface block variable. If arraySize is not zero,
+  /// The created variable will be an array of gl_PerVertex of the given size.
+  /// Otherwise, it will just be a plain struct.
+  uint32_t createBlockVar(bool asInput, uint32_t arraySize);
+  /// Creates a stand-alone Position builtin variable.
+  uint32_t createPositionVar(bool asInput);
+  /// Creates a stand-alone ClipDistance builtin variable.
+  uint32_t createClipDistanceVar(bool asInput, uint32_t arraySize);
+  /// Creates a stand-alone CullDistance builtin variable.
+  uint32_t createCullDistanceVar(bool asInput, uint32_t arraySize);
+
+  /// Emits SPIR-V instructions for reading the Position builtin.
+  uint32_t readPosition() const;
+  /// Emits SPIR-V instructions for reading the data starting from offset in
+  /// the ClipDistance/CullDistance builtin. The data read will be transformed
+  /// into the given type asType.
+  uint32_t readClipCullArrayAsType(bool isClip, uint32_t offset,
+                                   QualType asType) const;
+  /// Emits SPIR-V instructions to read a field in gl_PerVertex.
+  bool readField(hlsl::Semantic::Kind semanticKind, uint32_t semanticIndex,
+                 uint32_t *value);
+
+  /// Emits SPIR-V instructions for writing the Position builtin.
+  void writePosition(llvm::Optional<uint32_t> invocationId,
+                     uint32_t value) const;
+  /// Emits SPIR-V instructions for writing data into the ClipDistance/
+  /// CullDistance builtin starting from offset. The value to be written is
+  /// fromValue, whose type is fromType. Necessary transformations will be
+  /// generated to make sure type correctness.
+  void writeClipCullArrayFromType(llvm::Optional<uint32_t> invocationId,
+                                  bool isClip, uint32_t offset,
+                                  QualType fromType, uint32_t fromValue) const;
+  /// Emits SPIR-V instructions to write a field in gl_PerVertex.
+  bool writeField(hlsl::Semantic::Kind semanticKind, uint32_t semanticIndex,
+
+                  llvm::Optional<uint32_t> invocationId, uint32_t *value);
+
+  /// Internal implementation for recordClipCullDistanceDecl().
+  bool doClipCullDistanceDecl(const DeclaratorDecl *decl, QualType type,
+                              bool asInput);
+
+private:
+  using SemanticIndexToTypeMap = llvm::DenseMap<uint32_t, QualType>;
+  using SemanticIndexToArrayOffsetMap = llvm::DenseMap<uint32_t, uint32_t>;
+
+  const hlsl::ShaderModel &shaderModel;
+  ASTContext &astContext;
+  ModuleBuilder &theBuilder;
+  TypeTranslator &typeTranslator;
+
+  /// We can have Position, ClipDistance, and CullDistance either grouped (G)
+  /// into the gl_PerVertex struct, or separated (S) as stand-alone variables.
+  /// The following table shows for each shader stage, which one is used:
+  ///
+  /// ===== ===== ======
+  /// Stage Input Output
+  /// ===== ===== ======
+  ///  VS     X     G
+  ///  HS     G     G
+  ///  DS     G     G
+  ///  GS     G     S
+  ///  PS     S     X
+  /// ===== ===== ======
+  ///
+  /// Note that when we use separated variables, there is no extra arrayness.
+  ///
+  /// So depending on the shader stage, we may use one of the following set
+  /// of variables to store <result-id>s of the variables:
+
+  /// Indicates which set of variables are used.
+  bool inIsGrouped, outIsGrouped;
+  /// Input/output gl_PerVertex block variable if grouped.
+  uint32_t inBlockVar, outBlockVar;
+  /// Input/output ClipDistance/CullDistance variable if separated.
+  uint32_t inClipVar, inCullVar;
+  uint32_t outClipVar, outCullVar;
+
+  /// The array size for the input/output gl_PerVertex block variabe.
+  /// HS input and output, DS input, GS input has an additional level of
+  /// arrayness. The array size is stored in this variable. Zero means
+  /// the corresponding variable is a plain struct, not an array.
+  uint32_t inArraySize, outArraySize;
+  /// The array size of input/output ClipDistance/CullDistance float arrays.
+  /// This is not the array size of the whole gl_PerVertex struct.
+  uint32_t inClipArraySize, outClipArraySize;
+  uint32_t inCullArraySize, outCullArraySize;
+
+  /// We need to record all SV_ClipDistance/SV_CullDistance decls' types
+  /// since we need to generate the necessary conversion instructions when
+  /// accessing the ClipDistance/CullDistance builtins.
+  SemanticIndexToTypeMap inClipType, outClipType;
+  SemanticIndexToTypeMap inCullType, outCullType;
+  /// We also need to keep track of all SV_ClipDistance/SV_CullDistance decls'
+  /// offsets in the float array.
+  SemanticIndexToArrayOffsetMap inClipOffset, outClipOffset;
+  SemanticIndexToArrayOffsetMap inCullOffset, outCullOffset;
+};
+
+} // end namespace spirv
+} // end namespace clang
+
+#endif

+ 148 - 90
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -244,6 +244,23 @@ spv::Op translateAtomicHlslOpcodeToSpirvOpcode(hlsl::IntrinsicOp opcode) {
   return Op::Max;
 }
 
+/// Returns true if the given function parameter can act as shader stage
+/// input parameter.
+inline bool canActAsInParmVar(const ParmVarDecl *param) {
+  // If the parameter has no in/out/inout attribute, it is defaulted to
+  // an in parameter.
+  return !param->hasAttr<HLSLOutAttr>() &&
+         // GS output streams are marked as inout, but it should not be
+         // used as in parameter.
+         !hlsl::IsHLSLStreamOutputType(param->getType());
+}
+
+/// Returns true if the given function parameter can act as shader stage
+/// output parameter.
+inline bool canActAsOutParmVar(const ParmVarDecl *param) {
+  return param->hasAttr<HLSLOutAttr>() || param->hasAttr<HLSLInOutAttr>();
+}
+
 } // namespace
 
 SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci,
@@ -1290,7 +1307,7 @@ uint32_t SPIRVEmitter::processCall(const CallExpr *callExpr) {
       params.push_back(tempVarId);
       args.push_back(doExpr(arg));
 
-      if (param->getAttr<HLSLOutAttr>() || param->getAttr<HLSLInOutAttr>()) {
+      if (canActAsOutParmVar(param)) {
         // The current parameter is marked as out/inout. The argument then is
         // essentially passed in by reference. We need to load the value
         // explicitly here since the AST won't inject LValueToRValue implicit
@@ -1317,7 +1334,7 @@ uint32_t SPIRVEmitter::processCall(const CallExpr *callExpr) {
     // Go through all parameters and write those marked as out/inout
     for (uint32_t i = 0; i < numParams; ++i) {
       const auto *param = callee->getParamDecl(i);
-      if (param->getAttr<HLSLOutAttr>() || param->getAttr<HLSLInOutAttr>()) {
+      if (canActAsOutParmVar(param)) {
         const uint32_t index = i + isNonStaticMemberCall;
         const uint32_t typeId = typeTranslator.translateType(param->getType());
         const uint32_t value = theBuilder.createLoad(typeId, params[index]);
@@ -5451,7 +5468,8 @@ void SPIRVEmitter::AddExecutionModeForEntryPoint(uint32_t entryPointId) {
   }
 }
 
-bool SPIRVEmitter::processGeometryShaderAttributes(const FunctionDecl *decl) {
+bool SPIRVEmitter::processGeometryShaderAttributes(const FunctionDecl *decl,
+                                                   uint32_t *arraySize) {
   bool success = true;
   assert(shaderModel.IsGS());
   if (auto *vcAttr = decl->getAttr<HLSLMaxVertexCountAttr>()) {
@@ -5489,26 +5507,31 @@ bool SPIRVEmitter::processGeometryShaderAttributes(const FunctionDecl *decl) {
 
     // Add an execution mode based on the input primitive type. Do not add an
     // execution mode more than once.
-    if (param->hasAttr<HLSLTriangleAttr>() && !inTriangle) {
-      theBuilder.addExecutionMode(entryFunctionId,
-                                  spv::ExecutionMode::Triangles, {});
-      inTriangle = true;
-    } else if (param->hasAttr<HLSLTriangleAdjAttr>() && !inTriangleAdj) {
-      theBuilder.addExecutionMode(
-          entryFunctionId, spv::ExecutionMode::InputTrianglesAdjacency, {});
-      inTriangleAdj = true;
-    } else if (param->hasAttr<HLSLPointAttr>() && !inPoint) {
+    if (param->hasAttr<HLSLPointAttr>() && !inPoint) {
       theBuilder.addExecutionMode(entryFunctionId,
                                   spv::ExecutionMode::InputPoints, {});
+      *arraySize = 1;
       inPoint = true;
-    } else if (param->hasAttr<HLSLLineAdjAttr>() && !inLineAdj) {
-      theBuilder.addExecutionMode(entryFunctionId,
-                                  spv::ExecutionMode::InputLinesAdjacency, {});
-      inLineAdj = true;
     } else if (param->hasAttr<HLSLLineAttr>() && !inLine) {
       theBuilder.addExecutionMode(entryFunctionId,
                                   spv::ExecutionMode::InputLines, {});
+      *arraySize = 2;
       inLine = true;
+    } else if (param->hasAttr<HLSLTriangleAttr>() && !inTriangle) {
+      theBuilder.addExecutionMode(entryFunctionId,
+                                  spv::ExecutionMode::Triangles, {});
+      *arraySize = 3;
+      inTriangle = true;
+    } else if (param->hasAttr<HLSLLineAdjAttr>() && !inLineAdj) {
+      theBuilder.addExecutionMode(entryFunctionId,
+                                  spv::ExecutionMode::InputLinesAdjacency, {});
+      *arraySize = 4;
+      inLineAdj = true;
+    } else if (param->hasAttr<HLSLTriangleAdjAttr>() && !inTriangleAdj) {
+      theBuilder.addExecutionMode(
+          entryFunctionId, spv::ExecutionMode::InputTrianglesAdjacency, {});
+      *arraySize = 6;
+      inTriangleAdj = true;
     }
   }
   if (inPoint + inLine + inLineAdj + inTriangle + inTriangleAdj > 1) {
@@ -5525,6 +5548,20 @@ bool SPIRVEmitter::processGeometryShaderAttributes(const FunctionDecl *decl) {
   return success;
 }
 
+void SPIRVEmitter::processComputeShaderAttributes(const FunctionDecl *decl) {
+  // If not explicitly specified, x, y, and z should be defaulted to 1.
+  uint32_t x = 1, y = 1, z = 1;
+
+  if (auto *numThreadsAttr = decl->getAttr<HLSLNumThreadsAttr>()) {
+    x = static_cast<uint32_t>(numThreadsAttr->getX());
+    y = static_cast<uint32_t>(numThreadsAttr->getY());
+    z = static_cast<uint32_t>(numThreadsAttr->getZ());
+  }
+
+  theBuilder.addExecutionMode(entryFunctionId, spv::ExecutionMode::LocalSize,
+                              {x, y, z});
+}
+
 bool SPIRVEmitter::processTessellationShaderAttributes(
     const FunctionDecl *decl, uint32_t *numOutputControlPoints) {
   assert(shaderModel.IsHS() || shaderModel.IsDS());
@@ -5600,11 +5637,16 @@ bool SPIRVEmitter::processTessellationShaderAttributes(
 
 bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
                                             const uint32_t entryFuncId) {
-  // These are going to be used for Hull shaders only.
+  // HS specific attributes
   uint32_t numOutputControlPoints = 0;
-  uint32_t outputControlPointIdVal = 0;
-  uint32_t primitiveIdVar = 0;
-  uint32_t hullMainInputPatchParam = 0;
+  uint32_t outputControlPointIdVal = 0; // SV_OutputControlPointID value
+  uint32_t primitiveIdVar = 0;          // SV_PrimitiveID variable
+  uint32_t hullMainInputPatchParam = 0; // Temporary parameter for InputPatch<>
+
+  // The array size of per-vertex input/output variables
+  // Used by HS/DS/GS for the additional arrayness, zero means not an array.
+  uint32_t inputArraySize = 0;
+  uint32_t outputArraySize = 0;
 
   // Construct the wrapper function signature.
   const uint32_t voidType = theBuilder.getVoidType();
@@ -5615,28 +5657,69 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
   // function calls. And the wrapper is the entry function.
   entryFunctionId =
       theBuilder.beginFunction(funcType, voidType, decl->getName());
+  // Note this should happen before using declIdMapper for other tasks.
   declIdMapper.setEntryFunctionId(entryFunctionId);
 
-  // Handle translation of numthreads attribute for compute shaders.
+  // Handle attributes specific to each shader stage
   if (shaderModel.IsCS()) {
-    // Number of threads attributes are stored as integers. We cast them to
-    // uint32_t to pass to OpExecutionMode SPIR-V instruction.
-    if (auto *numThreadsAttr = decl->getAttr<HLSLNumThreadsAttr>()) {
-      theBuilder.addExecutionMode(
-          entryFunctionId, spv::ExecutionMode::LocalSize,
-          {static_cast<uint32_t>(numThreadsAttr->getX()),
-           static_cast<uint32_t>(numThreadsAttr->getY()),
-           static_cast<uint32_t>(numThreadsAttr->getZ())});
-    } else {
-      theBuilder.addExecutionMode(entryFunctionId,
-                                  spv::ExecutionMode::LocalSize, {1, 1, 1});
-    }
-  } else if (shaderModel.IsHS() || shaderModel.IsDS()) {
+    processComputeShaderAttributes(decl);
+  } else if (shaderModel.IsHS()) {
+    if (!processTessellationShaderAttributes(decl, &numOutputControlPoints))
+      return false;
+
+    // The input array size for HS is specified in the InputPatch parameter.
+    for (const auto *param : decl->params())
+      if (hlsl::IsHLSLInputPatchType(param->getType())) {
+        inputArraySize = hlsl::GetHLSLInputPatchCount(param->getType());
+        break;
+      }
+
+    outputArraySize = numOutputControlPoints;
+  } else if (shaderModel.IsDS()) {
     if (!processTessellationShaderAttributes(decl, &numOutputControlPoints))
       return false;
+
+    // The input array size for HS is specified in the OutputPatch parameter.
+    for (const auto *param : decl->params())
+      if (hlsl::IsHLSLOutputPatchType(param->getType())) {
+        inputArraySize = hlsl::GetHLSLOutputPatchCount(param->getType());
+        break;
+      }
+    // The per-vertex output of DS is not an array.
   } else if (shaderModel.IsGS()) {
-    if (!processGeometryShaderAttributes(decl))
+    if (!processGeometryShaderAttributes(decl, &inputArraySize))
       return false;
+    // The per-vertex output of GS is not an array.
+  }
+
+  // Go through all parameters and record the declaration of SV_ClipDistance
+  // and SV_CullDistance. We need to do this extra step because in HLSL we
+  // can declare multiple SV_ClipDistance/SV_CullDistance variables of float
+  // or vector of float types, but we can only have one single float array
+  // for the ClipDistance/CullDistance builtin. So we need to group all
+  // SV_ClipDistance/SV_CullDistance variables into one float array, thus we
+  // need to calculate the total size of the array and the offset of each
+  // variable within that array.
+  for (const auto *param : decl->params()) {
+    if (canActAsInParmVar(param))
+      if (!declIdMapper.glPerVertex.recordClipCullDistanceDecl(param, true))
+        return false;
+    if (canActAsOutParmVar(param))
+      if (!declIdMapper.glPerVertex.recordClipCullDistanceDecl(param, false))
+        return false;
+  }
+  // Also consider the SV_ClipDistance/SV_CullDistance in the return type
+  if (!declIdMapper.glPerVertex.recordClipCullDistanceDecl(decl, false))
+    return false;
+
+  // Calculate the total size of the ClipDistance/CullDistance array and the
+  // offset of SV_ClipDistance/SV_CullDistance variables within the array.
+  declIdMapper.glPerVertex.calculateClipCullDistanceArraySize();
+
+  if (!shaderModel.IsCS()) {
+    // Generate the gl_PerVertex structs or stand-alone builtins of
+    // Position, ClipDistance, and CullDistance.
+    declIdMapper.glPerVertex.generateVars(inputArraySize, outputArraySize);
   }
 
   // The entry basic block.
@@ -5651,7 +5734,8 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
   // Create temporary variables for holding function call arguments
   llvm::SmallVector<uint32_t, 4> params;
   for (const auto *param : decl->params()) {
-    const uint32_t typeId = typeTranslator.translateType(param->getType());
+    const auto paramType = param->getType();
+    const uint32_t typeId = typeTranslator.translateType(paramType);
     std::string tempVarName = "param.var." + param->getNameAsString();
     const uint32_t tempVar = theBuilder.addFnVar(typeId, tempVarName);
 
@@ -5661,36 +5745,21 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
     // initialize the corresponding temporary variable
     // Also do not create input variables for output stream objects of geometry
     // shaders (e.g. TriangleStream) which are required to be marked as 'inout'.
-    bool isGSOutputStream = shaderModel.IsGS() &&
-                            param->hasAttr<HLSLInOutAttr>() &&
-                            hlsl::IsHLSLStreamOutputType(param->getType());
-    if (!param->hasAttr<HLSLOutAttr>() && !isGSOutputStream) {
-      uint32_t loadedValue = 0;
-      if (shaderModel.IsHS() && hlsl::IsHLSLInputPatchType(param->getType())) {
-        const uint32_t hullInputPatchId =
-            declIdMapper.createStageVarWithoutSemantics(
-                /*isInput*/ true, typeId, "hullEntryPointInput",
-                decl->getAttr<VKLocationAttr>());
-        loadedValue = theBuilder.createLoad(typeId, hullInputPatchId);
+    if (canActAsInParmVar(param)) {
+      if (shaderModel.IsHS() && hlsl::IsHLSLInputPatchType(paramType)) {
+        // Record the temporary variable holding InputPatch. It may be used
+        // later in the patch constant function.
         hullMainInputPatchParam = tempVar;
-      } else if (shaderModel.IsDS() &&
-                 hlsl::IsHLSLOutputPatchType(param->getType())) {
-        // OutputPatch is the output of the hull shader and an input to the
-        // domain shader.
-        const uint32_t hullOutputPatchId =
-            declIdMapper.createStageVarWithoutSemantics(
-                /*isInput*/ true, typeId, "hullShaderOutput",
-                decl->getAttr<VKLocationAttr>());
-        loadedValue = theBuilder.createLoad(typeId, hullOutputPatchId);
-      } else if (!declIdMapper.createStageInputVar(param, &loadedValue,
-                                                   /*isPC*/ false)) {
-        return false;
       }
 
-      // SV_DomainLocation refers to a float2 (u,v), whereas TessCoord is a
-      // float3 (u,v,w). To ensure SPIR-V validity, a float3 stage variable is
-      // created, and we must extract a float2 from it before passing it to the
-      // main function.
+      uint32_t loadedValue = 0;
+
+      if (!declIdMapper.createStageInputVar(param, &loadedValue, false))
+        return false;
+
+      // SV_DomainLocation can refer to a float2, whereas TessCoord is a float3.
+      // To ensure SPIR-V validity, a float3 stage variable is created, and we
+      // must extract a float2 from it before passing it to the main function.
       if (hasSemantic(param, hlsl::DXIL::SemanticKind::DomainLocation) &&
           hlsl::GetHLSLVecSize(param->getType()) == 2) {
         loadedValue = theBuilder.createVectorShuffle(typeId, loadedValue,
@@ -5699,6 +5768,8 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
 
       theBuilder.createStore(tempVar, loadedValue);
 
+      // Record the temporary variable holding SV_OutputControlPointID and
+      // SV_PrimitiveID. It may be used later in the patch constant function.
       if (hasSemantic(param, hlsl::DXIL::SemanticKind::OutputControlPointID))
         outputControlPointIdVal = loadedValue;
       if (hasSemantic(param, hlsl::DXIL::SemanticKind::PrimitiveID))
@@ -5714,10 +5785,14 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
   // Create and write stage output variables for return value. Special case for
   // Hull shaders since they operate differently in 2 ways:
   // 1- Their return value is in fact an array and each invocation should write
-  // to the proper offset in the array.
+  //    to the proper offset in the array.
   // 2- The patch constant function must be called *once* after all invocations
-  // of the main entry point function is done.
+  //    of the main entry point function is done.
   if (shaderModel.IsHS()) {
+    // Create stage output variables out of the return type.
+    if (!declIdMapper.createStageOutputVar(decl, numOutputControlPoints,
+                                           outputControlPointIdVal, retVal))
+      return false;
     if (!processHullEntryPointOutputAndPatchConstFunc(
             decl, retType, retVal, numOutputControlPoints,
             outputControlPointIdVal, primitiveIdVar, hullMainInputPatchParam))
@@ -5731,13 +5806,12 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
   // out/inout
   for (uint32_t i = 0; i < decl->getNumParams(); ++i) {
     const auto *param = decl->getParamDecl(i);
-    if (param->getAttr<HLSLOutAttr>() || param->getAttr<HLSLInOutAttr>()) {
+    if (canActAsOutParmVar(param)) {
       // Load the value from the parameter after function call
       const uint32_t typeId = typeTranslator.translateType(param->getType());
       const uint32_t loadedParam = theBuilder.createLoad(typeId, params[i]);
 
-      if (!declIdMapper.createStageOutputVar(param, loadedParam,
-                                             /*isPC*/ false))
+      if (!declIdMapper.createStageOutputVar(param, loadedParam, false))
         return false;
     }
   }
@@ -5757,10 +5831,8 @@ bool SPIRVEmitter::processHullEntryPointOutputAndPatchConstFunc(
     const FunctionDecl *hullMainFuncDecl, uint32_t retType, uint32_t retVal,
     uint32_t numOutputControlPoints, uint32_t outputControlPointId,
     uint32_t primitiveId, uint32_t hullMainInputPatch) {
-
   // This method may only be called for Hull shaders.
   assert(shaderModel.IsHS());
-  uint32_t hullMainOutputPatch = 0;
 
   // For Hull shaders, the real output is an array of size
   // numOutputControlPoints. The results of the main should be written to the
@@ -5774,7 +5846,7 @@ bool SPIRVEmitter::processHullEntryPointOutputAndPatchConstFunc(
   // is not provided.
   if (!outputControlPointId) {
     emitError(
-        "SV_OutputControlPointID semantic must be provided in the hull shader",
+        "SV_OutputControlPointID semantic must be provided in hull shader",
         hullMainFuncDecl->getLocation());
     return false;
   }
@@ -5784,29 +5856,15 @@ bool SPIRVEmitter::processHullEntryPointOutputAndPatchConstFunc(
     return false;
   }
 
-  // Let's call the return value of the Hull entry point function
-  // "hllEntryPointOutput". The type of hullEntryPointOutput should be an
-  // array of size numOutputControlPoints.
-  const uint32_t hullEntryPointOutputType = theBuilder.getArrayType(
-      retType, theBuilder.getConstantUint32(numOutputControlPoints));
-  const auto loc = hullMainFuncDecl->getAttr<VKLocationAttr>();
-  const auto hullOutputVar = declIdMapper.createStageVarWithoutSemantics(
-      /*isInput*/ false, hullEntryPointOutputType, "hullEntryPointOutput", loc);
-  if (!hullOutputVar)
-    return false;
-
-  // Write the results into the correct Output array offset.
-  const auto location = theBuilder.createAccessChain(
-      theBuilder.getPointerType(retType, spv::StorageClass::Output),
-      hullOutputVar, {outputControlPointId});
-  theBuilder.createStore(location, retVal);
-
+  uint32_t hullMainOutputPatch = 0;
   // If the patch constant function (PCF) takes the result of the Hull main
   // entry point, create a temporary function-scope variable and write the
   // results to it, so it can be passed to the PCF.
   if (patchConstFuncTakesHullOutputPatch(patchConstFunc)) {
-    hullMainOutputPatch = theBuilder.addFnVar(hullEntryPointOutputType,
-                                              "temp.var.hullEntryPointOutput");
+    const uint32_t hullMainRetType = theBuilder.getArrayType(
+        retType, theBuilder.getConstantUint32(numOutputControlPoints));
+    hullMainOutputPatch =
+        theBuilder.addFnVar(hullMainRetType, "temp.var.hullMainRetVal");
     const auto tempLocation = theBuilder.createAccessChain(
         theBuilder.getPointerType(retType, spv::StorageClass::Function),
         hullMainOutputPatch, {outputControlPointId});

+ 7 - 1
tools/clang/lib/SPIRV/SPIRVEmitter.h

@@ -396,8 +396,14 @@ private:
                                            uint32_t *numOutputControlPoints);
 
   /// \brief Adds necessary execution modes for the geometry shader based on the
+  /// HLSL attributes of the entry point function. Also writes the array size of
+  /// the input, which depends on the primitive type, to *arraySize.
+  bool processGeometryShaderAttributes(const FunctionDecl *entryFunction,
+                                       uint32_t *arraySize);
+
+  /// \brief Adds necessary execution modes for the compute shader based on the
   /// HLSL attributes of the entry point function.
-  bool processGeometryShaderAttributes(const FunctionDecl *entryFunction);
+  void processComputeShaderAttributes(const FunctionDecl *entryFunction);
 
   /// \brief Emits a wrapper function for the entry function and returns true
   /// on success.

+ 26 - 0
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -276,6 +276,32 @@ uint32_t TypeTranslator::getACSBufferCounter() {
                                   decorations);
 }
 
+uint32_t TypeTranslator::getGlPerVertexStruct(uint32_t clipArraySize,
+                                              uint32_t cullArraySize,
+                                              llvm::StringRef name) {
+  const uint32_t f32Type = theBuilder.getFloat32Type();
+  const uint32_t v4f32Type = theBuilder.getVecType(f32Type, 4);
+  const uint32_t clipType = theBuilder.getArrayType(
+      f32Type, theBuilder.getConstantUint32(clipArraySize));
+  const uint32_t cullType = theBuilder.getArrayType(
+      f32Type, theBuilder.getConstantUint32(cullArraySize));
+
+  auto &ctx = *theBuilder.getSPIRVContext();
+  llvm::SmallVector<const Decoration *, 1> decorations;
+
+  decorations.push_back(Decoration::getBuiltIn(ctx, spv::BuiltIn::Position, 0));
+  decorations.push_back(
+      Decoration::getBuiltIn(ctx, spv::BuiltIn::PointSize, 1));
+  decorations.push_back(
+      Decoration::getBuiltIn(ctx, spv::BuiltIn::ClipDistance, 2));
+  decorations.push_back(
+      Decoration::getBuiltIn(ctx, spv::BuiltIn::CullDistance, 3));
+  decorations.push_back(Decoration::getBlock(ctx));
+
+  return theBuilder.getStructType({v4f32Type, f32Type, clipType, cullType},
+                                  name, {}, decorations);
+}
+
 bool TypeTranslator::isScalarType(QualType type, QualType *scalarType) {
   bool isScalar = false;
   QualType ty = {};

+ 11 - 0
tools/clang/lib/SPIRV/TypeTranslator.h

@@ -54,6 +54,17 @@ public:
   /// integer value. This type will be decorated with BufferBlock.
   uint32_t getACSBufferCounter();
 
+  /// \brief Returns the type for the gl_PerVertex struct:
+  ///
+  /// struct gl_PerVertex {
+  ///   float4 gl_Position;
+  ///   float  gl_PointSize;
+  ///   float  gl_ClipDistance[];
+  ///   float  gl_CullDistance[];
+  /// };
+  uint32_t getGlPerVertexStruct(uint32_t clipArraySize, uint32_t cullArraySize,
+                                llvm::StringRef structName);
+
   /// \brief Returns true if the given type is a (RW)StructuredBuffer type.
   static bool isStructuredBuffer(QualType type);
 

+ 70 - 47
tools/clang/test/CodeGenSPIRV/bezier.domain.hlsl2spv

@@ -1,13 +1,13 @@
 // Run: %dxc -T ds_6_0 -E BezierEvalDS
 
-#define MAX_POINTS 16
+#define MAX_POINTS 4
 
 // Output patch constant data (output of hull shader)
 struct HS_CONSTANT_DATA_OUTPUT
 {
   float Edges[4]        : SV_TessFactor;
   float Inside[2]       : SV_InsideTessFactor;
-  
+
   float3 vTangent[4]    : TANGENT;
   float2 vUV[4]         : TEXCOORD;
   float3 vTanUCorner[4] : TANUCORNER;
@@ -34,7 +34,7 @@ struct DS_OUTPUT
 
 
 [domain("quad")]
-DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input, 
+DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
                         float2 UV : SV_DomainLocation,
                         const OutputPatch<BEZIER_CONTROL_POINT, MAX_POINTS> bezpatch )
 {
@@ -48,11 +48,14 @@ DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
 // CHECK-WHOLE-SPIR-V:
 // OpCapability Tessellation
 // OpMemoryModel Logical GLSL450
-// OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS" %gl_TessLevelOuter %gl_TessLevelInner %in_var_TANGENT %in_var_TEXCOORD %in_var_TANUCORNER %in_var_TANVCORNER %in_var_TANWEIGHTS %gl_TessCoord %in_var_hullShaderOutput %out_var_NORMAL %out_var_TEXCOORD %out_var_TANGENT %out_var_BITANGENT %gl_Position
+// OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS" %gl_PerVertexIn %gl_PerVertexOut %gl_TessLevelOuter %gl_TessLevelInner %in_var_TANGENT %in_var_TEXCOORD %in_var_TANUCORNER %in_var_TANVCORNER %in_var_TANWEIGHTS %gl_TessCoord %in_var_BEZIERPOS %out_var_NORMAL %out_var_TEXCOORD %out_var_TANGENT %out_var_BITANGENT
 // OpExecutionMode %BezierEvalDS Quads
 // OpName %bb_entry "bb.entry"
 // OpName %src_BezierEvalDS "src.BezierEvalDS"
 // OpName %BezierEvalDS "BezierEvalDS"
+// OpName %type_gl_PerVertex "type.gl_PerVertex"
+// OpName %gl_PerVertexIn "gl_PerVertexIn"
+// OpName %gl_PerVertexOut "gl_PerVertexOut"
 // OpName %HS_CONSTANT_DATA_OUTPUT "HS_CONSTANT_DATA_OUTPUT"
 // OpMemberName %HS_CONSTANT_DATA_OUTPUT 0 "Edges"
 // OpMemberName %HS_CONSTANT_DATA_OUTPUT 1 "Inside"
@@ -71,7 +74,7 @@ DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
 // OpName %BEZIER_CONTROL_POINT "BEZIER_CONTROL_POINT"
 // OpMemberName %BEZIER_CONTROL_POINT 0 "vPosition"
 // OpName %param_var_bezpatch "param.var.bezpatch"
-// OpName %in_var_hullShaderOutput "in.var.hullShaderOutput"
+// OpName %in_var_BEZIERPOS "in.var.BEZIERPOS"
 // OpName %DS_OUTPUT "DS_OUTPUT"
 // OpMemberName %DS_OUTPUT 0 "vNormal"
 // OpMemberName %DS_OUTPUT 1 "vUV"
@@ -86,6 +89,11 @@ DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
 // OpName %UV "UV"
 // OpName %bezpatch "bezpatch"
 // OpName %Output "Output"
+// OpMemberDecorate %type_gl_PerVertex 0 BuiltIn Position
+// OpMemberDecorate %type_gl_PerVertex 1 BuiltIn PointSize
+// OpMemberDecorate %type_gl_PerVertex 2 BuiltIn ClipDistance
+// OpMemberDecorate %type_gl_PerVertex 3 BuiltIn CullDistance
+// OpDecorate %type_gl_PerVertex Block
 // OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
 // OpDecorate %gl_TessLevelOuter Patch
 // OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
@@ -97,13 +105,12 @@ DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
 // OpDecorate %in_var_TANWEIGHTS Patch
 // OpDecorate %gl_TessCoord BuiltIn TessCoord
 // OpDecorate %gl_TessCoord Patch
-// OpDecorate %gl_Position BuiltIn Position
 // OpDecorate %in_var_TANGENT Location 0
 // OpDecorate %in_var_TEXCOORD Location 1
 // OpDecorate %in_var_TANUCORNER Location 2
 // OpDecorate %in_var_TANVCORNER Location 3
 // OpDecorate %in_var_TANWEIGHTS Location 4
-// OpDecorate %in_var_hullShaderOutput Location 5
+// OpDecorate %in_var_BEZIERPOS Location 5
 // OpDecorate %out_var_NORMAL Location 0
 // OpDecorate %out_var_TEXCOORD Location 1
 // OpDecorate %out_var_TANGENT Location 2
@@ -112,7 +119,14 @@ DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
 // %void = OpTypeVoid
 // %3 = OpTypeFunction %void
 // %float = OpTypeFloat 32
+// %v4float = OpTypeVector %float 4
+// %uint_1 = OpConstant %uint 1
+// %_arr_float_uint_1 = OpTypeArray %float %uint_1
+// %type_gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
 // %uint_4 = OpConstant %uint 4
+// %_arr_type_gl_PerVertex_uint_4 = OpTypeArray %type_gl_PerVertex %uint_4
+// %_ptr_Input__arr_type_gl_PerVertex_uint_4 = OpTypePointer Input %_arr_type_gl_PerVertex_uint_4
+// %_ptr_Output_type_gl_PerVertex = OpTypePointer Output %type_gl_PerVertex
 // %_arr_float_uint_4 = OpTypeArray %float %uint_4
 // %uint_2 = OpConstant %uint 2
 // %_arr_float_uint_2 = OpTypeArray %float %uint_2
@@ -120,7 +134,6 @@ DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
 // %_arr_v3float_uint_4 = OpTypeArray %v3float %uint_4
 // %v2float = OpTypeVector %float 2
 // %_arr_v2float_uint_4 = OpTypeArray %v2float %uint_4
-// %v4float = OpTypeVector %float 4
 // %HS_CONSTANT_DATA_OUTPUT = OpTypeStruct %_arr_float_uint_4 %_arr_float_uint_2 %_arr_v3float_uint_4 %_arr_v2float_uint_4 %_arr_v3float_uint_4 %_arr_v3float_uint_4 %v4float
 // %_ptr_Function_HS_CONSTANT_DATA_OUTPUT = OpTypePointer Function %HS_CONSTANT_DATA_OUTPUT
 // %_ptr_Input__arr_float_uint_4 = OpTypePointer Input %_arr_float_uint_4
@@ -131,16 +144,17 @@ DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
 // %_ptr_Function_v2float = OpTypePointer Function %v2float
 // %_ptr_Input_v3float = OpTypePointer Input %v3float
 // %BEZIER_CONTROL_POINT = OpTypeStruct %v3float
-// %uint_16 = OpConstant %uint 16
-// %_arr_BEZIER_CONTROL_POINT_uint_16 = OpTypeArray %BEZIER_CONTROL_POINT %uint_16
-// %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16 = OpTypePointer Function %_arr_BEZIER_CONTROL_POINT_uint_16
-// %_ptr_Input__arr_BEZIER_CONTROL_POINT_uint_16 = OpTypePointer Input %_arr_BEZIER_CONTROL_POINT_uint_16
+// %_arr_BEZIER_CONTROL_POINT_uint_4 = OpTypeArray %BEZIER_CONTROL_POINT %uint_4
+// %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_4 = OpTypePointer Function %_arr_BEZIER_CONTROL_POINT_uint_4
 // %DS_OUTPUT = OpTypeStruct %v3float %v2float %v3float %v3float %v4float
 // %_ptr_Output_v3float = OpTypePointer Output %v3float
 // %_ptr_Output_v2float = OpTypePointer Output %v2float
 // %_ptr_Output_v4float = OpTypePointer Output %v4float
-// %69 = OpTypeFunction %DS_OUTPUT %_ptr_Function_HS_CONSTANT_DATA_OUTPUT %_ptr_Function_v2float %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16
+// %85 = OpTypeFunction %DS_OUTPUT %_ptr_Function_HS_CONSTANT_DATA_OUTPUT %_ptr_Function_v2float %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_4
 // %_ptr_Function_DS_OUTPUT = OpTypePointer Function %DS_OUTPUT
+// %uint_0 = OpConstant %uint 0
+// %gl_PerVertexIn = OpVariable %_ptr_Input__arr_type_gl_PerVertex_uint_4 Input
+// %gl_PerVertexOut = OpVariable %_ptr_Output_type_gl_PerVertex Output
 // %gl_TessLevelOuter = OpVariable %_ptr_Input__arr_float_uint_4 Input
 // %gl_TessLevelInner = OpVariable %_ptr_Input__arr_float_uint_2 Input
 // %in_var_TANGENT = OpVariable %_ptr_Input__arr_v3float_uint_4 Input
@@ -149,50 +163,59 @@ DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
 // %in_var_TANVCORNER = OpVariable %_ptr_Input__arr_v3float_uint_4 Input
 // %in_var_TANWEIGHTS = OpVariable %_ptr_Input_v4float Input
 // %gl_TessCoord = OpVariable %_ptr_Input_v3float Input
-// %in_var_hullShaderOutput = OpVariable %_ptr_Input__arr_BEZIER_CONTROL_POINT_uint_16 Input
+// %in_var_BEZIERPOS = OpVariable %_ptr_Input__arr_v3float_uint_4 Input
 // %out_var_NORMAL = OpVariable %_ptr_Output_v3float Output
 // %out_var_TEXCOORD = OpVariable %_ptr_Output_v2float Output
 // %out_var_TANGENT = OpVariable %_ptr_Output_v3float Output
 // %out_var_BITANGENT = OpVariable %_ptr_Output_v3float Output
-// %gl_Position = OpVariable %_ptr_Output_v4float Output
 // %BezierEvalDS = OpFunction %void None %3
-// %5 = OpLabel
+// %17 = OpLabel
 // %param_var_input = OpVariable %_ptr_Function_HS_CONSTANT_DATA_OUTPUT Function
 // %param_var_UV = OpVariable %_ptr_Function_v2float Function
-// %param_var_bezpatch = OpVariable %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16 Function
-// %22 = OpLoad %_arr_float_uint_4 %gl_TessLevelOuter
-// %25 = OpLoad %_arr_float_uint_2 %gl_TessLevelInner
-// %28 = OpLoad %_arr_v3float_uint_4 %in_var_TANGENT
-// %31 = OpLoad %_arr_v2float_uint_4 %in_var_TEXCOORD
-// %33 = OpLoad %_arr_v3float_uint_4 %in_var_TANUCORNER
-// %35 = OpLoad %_arr_v3float_uint_4 %in_var_TANVCORNER
-// %38 = OpLoad %v4float %in_var_TANWEIGHTS
-// %39 = OpCompositeConstruct %HS_CONSTANT_DATA_OUTPUT %22 %25 %28 %31 %33 %35 %38
-// OpStore %param_var_input %39
-// %44 = OpLoad %v3float %gl_TessCoord
-// %45 = OpVectorShuffle %v2float %44 %44 0 1
-// OpStore %param_var_UV %45
-// %53 = OpLoad %_arr_BEZIER_CONTROL_POINT_uint_16 %in_var_hullShaderOutput
-// OpStore %param_var_bezpatch %53
-// %55 = OpFunctionCall %DS_OUTPUT %src_BezierEvalDS %param_var_input %param_var_UV %param_var_bezpatch
-// %56 = OpCompositeExtract %v3float %55 0
-// OpStore %out_var_NORMAL %56
-// %59 = OpCompositeExtract %v2float %55 1
-// OpStore %out_var_TEXCOORD %59
-// %62 = OpCompositeExtract %v3float %55 2
-// OpStore %out_var_TANGENT %62
-// %64 = OpCompositeExtract %v3float %55 3
-// OpStore %out_var_BITANGENT %64
-// %66 = OpCompositeExtract %v4float %55 4
-// OpStore %gl_Position %66
+// %param_var_bezpatch = OpVariable %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_4 Function
+// %30 = OpLoad %_arr_float_uint_4 %gl_TessLevelOuter
+// %33 = OpLoad %_arr_float_uint_2 %gl_TessLevelInner
+// %36 = OpLoad %_arr_v3float_uint_4 %in_var_TANGENT
+// %39 = OpLoad %_arr_v2float_uint_4 %in_var_TEXCOORD
+// %41 = OpLoad %_arr_v3float_uint_4 %in_var_TANUCORNER
+// %43 = OpLoad %_arr_v3float_uint_4 %in_var_TANVCORNER
+// %46 = OpLoad %v4float %in_var_TANWEIGHTS
+// %47 = OpCompositeConstruct %HS_CONSTANT_DATA_OUTPUT %30 %33 %36 %39 %41 %43 %46
+// OpStore %param_var_input %47
+// %52 = OpLoad %v3float %gl_TessCoord
+// %53 = OpVectorShuffle %v2float %52 %52 0 1
+// OpStore %param_var_UV %53
+// %59 = OpLoad %_arr_v3float_uint_4 %in_var_BEZIERPOS
+// %60 = OpCompositeExtract %v3float %59 0
+// %61 = OpCompositeConstruct %BEZIER_CONTROL_POINT %60
+// %62 = OpCompositeExtract %v3float %59 1
+// %63 = OpCompositeConstruct %BEZIER_CONTROL_POINT %62
+// %64 = OpCompositeExtract %v3float %59 2
+// %65 = OpCompositeConstruct %BEZIER_CONTROL_POINT %64
+// %66 = OpCompositeExtract %v3float %59 3
+// %67 = OpCompositeConstruct %BEZIER_CONTROL_POINT %66
+// %68 = OpCompositeConstruct %_arr_BEZIER_CONTROL_POINT_uint_4 %61 %63 %65 %67
+// OpStore %param_var_bezpatch %68
+// %70 = OpFunctionCall %DS_OUTPUT %src_BezierEvalDS %param_var_input %param_var_UV %param_var_bezpatch
+// %71 = OpCompositeExtract %v3float %70 0
+// OpStore %out_var_NORMAL %71
+// %74 = OpCompositeExtract %v2float %70 1
+// OpStore %out_var_TEXCOORD %74
+// %77 = OpCompositeExtract %v3float %70 2
+// OpStore %out_var_TANGENT %77
+// %79 = OpCompositeExtract %v3float %70 3
+// OpStore %out_var_BITANGENT %79
+// %81 = OpCompositeExtract %v4float %70 4
+// %84 = OpAccessChain %_ptr_Output_v4float %gl_PerVertexOut %uint_0
+// OpStore %84 %81
 // OpReturn
 // OpFunctionEnd
-// %src_BezierEvalDS = OpFunction %DS_OUTPUT None %69
+// %src_BezierEvalDS = OpFunction %DS_OUTPUT None %85
 // %input = OpFunctionParameter %_ptr_Function_HS_CONSTANT_DATA_OUTPUT
 // %UV = OpFunctionParameter %_ptr_Function_v2float
-// %bezpatch = OpFunctionParameter %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16
+// %bezpatch = OpFunctionParameter %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_4
 // %bb_entry = OpLabel
 // %Output = OpVariable %_ptr_Function_DS_OUTPUT Function
-// %76 = OpLoad %DS_OUTPUT %Output
-// OpReturnValue %76
-// OpFunctionEnd
+// %92 = OpLoad %DS_OUTPUT %Output
+// OpReturnValue %92
+// OpFunctionEnd

+ 113 - 74
tools/clang/test/CodeGenSPIRV/bezier.hull.hlsl2spv

@@ -1,6 +1,6 @@
 // Run: %dxc -T hs_6_0 -E SubDToBezierHS
 
-#define MAX_POINTS 16
+#define MAX_POINTS 3
 
 // Input control point
 struct VS_CONTROL_POINT_OUTPUT
@@ -32,7 +32,7 @@ struct HS_CONSTANT_DATA_OUTPUT
 // Patch Constant Function
 HS_CONSTANT_DATA_OUTPUT SubDToBezierConstantsHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POINTS> ip, uint PatchID : SV_PrimitiveID) {
   HS_CONSTANT_DATA_OUTPUT Output;
-  
+
   // Must initialize Edges and Inside; otherwise HLSL validation will fail.
   Output.Edges[0]  = 1.0;
   Output.Edges[1]  = 2.0;
@@ -40,7 +40,7 @@ HS_CONSTANT_DATA_OUTPUT SubDToBezierConstantsHS(InputPatch<VS_CONTROL_POINT_OUTP
   Output.Edges[3]  = 4.0;
   Output.Inside[0] = 5.0;
   Output.Inside[1] = 6.0;
-  
+
   return Output;
 }
 
@@ -59,28 +59,33 @@ BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POIN
 // CHECK-WHOLE-SPIR-V:
 // OpCapability Tessellation
 // OpMemoryModel Logical GLSL450
-// OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS" %in_var_hullEntryPointInput %gl_InvocationID %gl_PrimitiveID %out_var_hullEntryPointOutput %gl_TessLevelOuter %gl_TessLevelInner %out_var_TANGENT %out_var_TEXCOORD %out_var_TANUCORNER %out_var_TANVCORNER %out_var_TANWEIGHTS
+// OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS" %gl_PerVertexIn %gl_PerVertexOut %in_var_WORLDPOS %in_var_TEXCOORD0 %in_var_TANGENT %gl_InvocationID %gl_PrimitiveID %out_var_BEZIERPOS %gl_TessLevelOuter %gl_TessLevelInner %out_var_TANGENT %out_var_TEXCOORD %out_var_TANUCORNER %out_var_TANVCORNER %out_var_TANWEIGHTS
 // OpExecutionMode %SubDToBezierHS Triangles
 // OpExecutionMode %SubDToBezierHS SpacingFractionalOdd
 // OpExecutionMode %SubDToBezierHS VertexOrderCcw
-// OpExecutionMode %SubDToBezierHS OutputVertices 16
+// OpExecutionMode %SubDToBezierHS OutputVertices 3
 // OpName %if_true "if.true"
 // OpName %if_merge "if.merge"
 // OpName %bb_entry "bb.entry"
 // OpName %bb_entry_0 "bb.entry"
 // OpName %src_SubDToBezierHS "src.SubDToBezierHS"
 // OpName %SubDToBezierHS "SubDToBezierHS"
+// OpName %type_gl_PerVertex "type.gl_PerVertex"
+// OpName %gl_PerVertexIn "gl_PerVertexIn"
+// OpName %gl_PerVertexOut "gl_PerVertexOut"
 // OpName %VS_CONTROL_POINT_OUTPUT "VS_CONTROL_POINT_OUTPUT"
 // OpMemberName %VS_CONTROL_POINT_OUTPUT 0 "vPosition"
 // OpMemberName %VS_CONTROL_POINT_OUTPUT 1 "vUV"
 // OpMemberName %VS_CONTROL_POINT_OUTPUT 2 "vTangent"
 // OpName %param_var_ip "param.var.ip"
-// OpName %in_var_hullEntryPointInput "in.var.hullEntryPointInput"
+// OpName %in_var_WORLDPOS "in.var.WORLDPOS"
+// OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0"
+// OpName %in_var_TANGENT "in.var.TANGENT"
 // OpName %param_var_cpid "param.var.cpid"
 // OpName %param_var_PatchID "param.var.PatchID"
 // OpName %BEZIER_CONTROL_POINT "BEZIER_CONTROL_POINT"
 // OpMemberName %BEZIER_CONTROL_POINT 0 "vPosition"
-// OpName %out_var_hullEntryPointOutput "out.var.hullEntryPointOutput"
+// OpName %out_var_BEZIERPOS "out.var.BEZIERPOS"
 // OpName %SubDToBezierConstantsHS "SubDToBezierConstantsHS"
 // OpName %HS_CONSTANT_DATA_OUTPUT "HS_CONSTANT_DATA_OUTPUT"
 // OpMemberName %HS_CONSTANT_DATA_OUTPUT 0 "Edges"
@@ -103,6 +108,11 @@ BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POIN
 // OpName %PatchID_0 "PatchID"
 // OpName %vsOutput "vsOutput"
 // OpName %result "result"
+// OpMemberDecorate %type_gl_PerVertex 0 BuiltIn Position
+// OpMemberDecorate %type_gl_PerVertex 1 BuiltIn PointSize
+// OpMemberDecorate %type_gl_PerVertex 2 BuiltIn ClipDistance
+// OpMemberDecorate %type_gl_PerVertex 3 BuiltIn CullDistance
+// OpDecorate %type_gl_PerVertex Block
 // OpDecorate %gl_InvocationID BuiltIn InvocationId
 // OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
 // OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
@@ -114,8 +124,10 @@ BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POIN
 // OpDecorate %out_var_TANUCORNER Patch
 // OpDecorate %out_var_TANVCORNER Patch
 // OpDecorate %out_var_TANWEIGHTS Patch
-// OpDecorate %in_var_hullEntryPointInput Location 0
-// OpDecorate %out_var_hullEntryPointOutput Location 0
+// OpDecorate %in_var_WORLDPOS Location 0
+// OpDecorate %in_var_TEXCOORD0 Location 1
+// OpDecorate %in_var_TANGENT Location 2
+// OpDecorate %out_var_BEZIERPOS Location 0
 // OpDecorate %out_var_TANGENT Location 1
 // OpDecorate %out_var_TEXCOORD Location 2
 // OpDecorate %out_var_TANUCORNER Location 3
@@ -126,19 +138,28 @@ BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POIN
 // %void = OpTypeVoid
 // %3 = OpTypeFunction %void
 // %float = OpTypeFloat 32
+// %v4float = OpTypeVector %float 4
+// %uint_1 = OpConstant %uint 1
+// %_arr_float_uint_1 = OpTypeArray %float %uint_1
+// %type_gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+// %uint_3 = OpConstant %uint 3
+// %_arr_type_gl_PerVertex_uint_3 = OpTypeArray %type_gl_PerVertex %uint_3
+// %_ptr_Input__arr_type_gl_PerVertex_uint_3 = OpTypePointer Input %_arr_type_gl_PerVertex_uint_3
+// %_ptr_Output__arr_type_gl_PerVertex_uint_3 = OpTypePointer Output %_arr_type_gl_PerVertex_uint_3
 // %v3float = OpTypeVector %float 3
 // %v2float = OpTypeVector %float 2
 // %VS_CONTROL_POINT_OUTPUT = OpTypeStruct %v3float %v2float %v3float
-// %uint_16 = OpConstant %uint 16
-// %_arr_VS_CONTROL_POINT_OUTPUT_uint_16 = OpTypeArray %VS_CONTROL_POINT_OUTPUT %uint_16
-// %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16 = OpTypePointer Function %_arr_VS_CONTROL_POINT_OUTPUT_uint_16
-// %_ptr_Input__arr_VS_CONTROL_POINT_OUTPUT_uint_16 = OpTypePointer Input %_arr_VS_CONTROL_POINT_OUTPUT_uint_16
+// %_arr_VS_CONTROL_POINT_OUTPUT_uint_3 = OpTypeArray %VS_CONTROL_POINT_OUTPUT %uint_3
+// %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_3 = OpTypePointer Function %_arr_VS_CONTROL_POINT_OUTPUT_uint_3
+// %_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3
+// %_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3
+// %_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3
+// %_ptr_Input__arr_v2float_uint_3 = OpTypePointer Input %_arr_v2float_uint_3
 // %_ptr_Function_uint = OpTypePointer Function %uint
 // %_ptr_Input_uint = OpTypePointer Input %uint
 // %BEZIER_CONTROL_POINT = OpTypeStruct %v3float
-// %_arr_BEZIER_CONTROL_POINT_uint_16 = OpTypeArray %BEZIER_CONTROL_POINT %uint_16
-// %_ptr_Output__arr_BEZIER_CONTROL_POINT_uint_16 = OpTypePointer Output %_arr_BEZIER_CONTROL_POINT_uint_16
-// %_ptr_Output_BEZIER_CONTROL_POINT = OpTypePointer Output %BEZIER_CONTROL_POINT
+// %_ptr_Output__arr_v3float_uint_3 = OpTypePointer Output %_arr_v3float_uint_3
+// %_ptr_Output_v3float = OpTypePointer Output %v3float
 // %bool = OpTypeBool
 // %uint_4 = OpConstant %uint 4
 // %_arr_float_uint_4 = OpTypeArray %float %uint_4
@@ -146,22 +167,20 @@ BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POIN
 // %_arr_float_uint_2 = OpTypeArray %float %uint_2
 // %_arr_v3float_uint_4 = OpTypeArray %v3float %uint_4
 // %_arr_v2float_uint_4 = OpTypeArray %v2float %uint_4
-// %v4float = OpTypeVector %float 4
 // %HS_CONSTANT_DATA_OUTPUT = OpTypeStruct %_arr_float_uint_4 %_arr_float_uint_2 %_arr_v3float_uint_4 %_arr_v2float_uint_4 %_arr_v3float_uint_4 %_arr_v3float_uint_4 %v4float
 // %_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4
 // %_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2
 // %_ptr_Output__arr_v3float_uint_4 = OpTypePointer Output %_arr_v3float_uint_4
 // %_ptr_Output__arr_v2float_uint_4 = OpTypePointer Output %_arr_v2float_uint_4
 // %_ptr_Output_v4float = OpTypePointer Output %v4float
-// %68 = OpTypeFunction %HS_CONSTANT_DATA_OUTPUT %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16 %_ptr_Function_uint
+// %95 = OpTypeFunction %HS_CONSTANT_DATA_OUTPUT %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_3 %_ptr_Function_uint
 // %_ptr_Function_HS_CONSTANT_DATA_OUTPUT = OpTypePointer Function %HS_CONSTANT_DATA_OUTPUT
 // %_ptr_Function_float = OpTypePointer Function %float
-// %94 = OpTypeFunction %BEZIER_CONTROL_POINT %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16 %_ptr_Function_uint %_ptr_Function_uint
+// %121 = OpTypeFunction %BEZIER_CONTROL_POINT %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_3 %_ptr_Function_uint %_ptr_Function_uint
 // %_ptr_Function_VS_CONTROL_POINT_OUTPUT = OpTypePointer Function %VS_CONTROL_POINT_OUTPUT
 // %_ptr_Function_BEZIER_CONTROL_POINT = OpTypePointer Function %BEZIER_CONTROL_POINT
 // %_ptr_Function_v3float = OpTypePointer Function %v3float
 // %uint_0 = OpConstant %uint 0
-// %uint_1 = OpConstant %uint 1
 // %float_1 = OpConstant %float 1
 // %int_0 = OpConstant %int 0
 // %float_2 = OpConstant %float 2
@@ -172,10 +191,14 @@ BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POIN
 // %int_3 = OpConstant %int 3
 // %float_5 = OpConstant %float 5
 // %float_6 = OpConstant %float 6
-// %in_var_hullEntryPointInput = OpVariable %_ptr_Input__arr_VS_CONTROL_POINT_OUTPUT_uint_16 Input
+// %gl_PerVertexIn = OpVariable %_ptr_Input__arr_type_gl_PerVertex_uint_3 Input
+// %gl_PerVertexOut = OpVariable %_ptr_Output__arr_type_gl_PerVertex_uint_3 Output
+// %in_var_WORLDPOS = OpVariable %_ptr_Input__arr_v3float_uint_3 Input
+// %in_var_TEXCOORD0 = OpVariable %_ptr_Input__arr_v2float_uint_3 Input
+// %in_var_TANGENT = OpVariable %_ptr_Input__arr_v3float_uint_3 Input
 // %gl_InvocationID = OpVariable %_ptr_Input_uint Input
 // %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input
-// %out_var_hullEntryPointOutput = OpVariable %_ptr_Output__arr_BEZIER_CONTROL_POINT_uint_16 Output
+// %out_var_BEZIERPOS = OpVariable %_ptr_Output__arr_v3float_uint_3 Output
 // %gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output
 // %gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output
 // %out_var_TANGENT = OpVariable %_ptr_Output__arr_v3float_uint_4 Output
@@ -184,74 +207,90 @@ BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POIN
 // %out_var_TANVCORNER = OpVariable %_ptr_Output__arr_v3float_uint_4 Output
 // %out_var_TANWEIGHTS = OpVariable %_ptr_Output_v4float Output
 // %SubDToBezierHS = OpFunction %void None %3
-// %5 = OpLabel
-// %param_var_ip = OpVariable %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16 Function
+// %17 = OpLabel
+// %param_var_ip = OpVariable %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_3 Function
 // %param_var_cpid = OpVariable %_ptr_Function_uint Function
 // %param_var_PatchID = OpVariable %_ptr_Function_uint Function
-// %17 = OpLoad %_arr_VS_CONTROL_POINT_OUTPUT_uint_16 %in_var_hullEntryPointInput
-// OpStore %param_var_ip %17
-// %22 = OpLoad %uint %gl_InvocationID
-// OpStore %param_var_cpid %22
-// %25 = OpLoad %uint %gl_PrimitiveID
-// OpStore %param_var_PatchID %25
-// %27 = OpFunctionCall %BEZIER_CONTROL_POINT %src_SubDToBezierHS %param_var_ip %param_var_cpid %param_var_PatchID
-// %32 = OpAccessChain %_ptr_Output_BEZIER_CONTROL_POINT %out_var_hullEntryPointOutput %22
-// OpStore %32 %27
+// %27 = OpLoad %_arr_v3float_uint_3 %in_var_WORLDPOS
+// %31 = OpLoad %_arr_v2float_uint_3 %in_var_TEXCOORD0
+// %33 = OpLoad %_arr_v3float_uint_3 %in_var_TANGENT
+// %34 = OpCompositeExtract %v3float %27 0
+// %35 = OpCompositeExtract %v2float %31 0
+// %36 = OpCompositeExtract %v3float %33 0
+// %37 = OpCompositeConstruct %VS_CONTROL_POINT_OUTPUT %34 %35 %36
+// %38 = OpCompositeExtract %v3float %27 1
+// %39 = OpCompositeExtract %v2float %31 1
+// %40 = OpCompositeExtract %v3float %33 1
+// %41 = OpCompositeConstruct %VS_CONTROL_POINT_OUTPUT %38 %39 %40
+// %42 = OpCompositeExtract %v3float %27 2
+// %43 = OpCompositeExtract %v2float %31 2
+// %44 = OpCompositeExtract %v3float %33 2
+// %45 = OpCompositeConstruct %VS_CONTROL_POINT_OUTPUT %42 %43 %44
+// %46 = OpCompositeConstruct %_arr_VS_CONTROL_POINT_OUTPUT_uint_3 %37 %41 %45
+// OpStore %param_var_ip %46
+// %51 = OpLoad %uint %gl_InvocationID
+// OpStore %param_var_cpid %51
+// %54 = OpLoad %uint %gl_PrimitiveID
+// OpStore %param_var_PatchID %54
+// %56 = OpFunctionCall %BEZIER_CONTROL_POINT %src_SubDToBezierHS %param_var_ip %param_var_cpid %param_var_PatchID
+// %57 = OpCompositeExtract %v3float %56 0
+// %61 = OpAccessChain %_ptr_Output_v3float %out_var_BEZIERPOS %51
+// OpStore %61 %57
 // OpControlBarrier %uint_2 %uint_1 %uint_0
-// %37 = OpIEqual %bool %22 %uint_0
+// %65 = OpIEqual %bool %51 %uint_0
 // OpSelectionMerge %if_merge None
-// OpBranchConditional %37 %if_true %if_merge
+// OpBranchConditional %65 %if_true %if_merge
 // %if_true = OpLabel
-// %48 = OpFunctionCall %HS_CONSTANT_DATA_OUTPUT %SubDToBezierConstantsHS %param_var_ip %param_var_PatchID
-// %49 = OpCompositeExtract %_arr_float_uint_4 %48 0
-// OpStore %gl_TessLevelOuter %49
-// %52 = OpCompositeExtract %_arr_float_uint_2 %48 1
-// OpStore %gl_TessLevelInner %52
-// %55 = OpCompositeExtract %_arr_v3float_uint_4 %48 2
-// OpStore %out_var_TANGENT %55
-// %58 = OpCompositeExtract %_arr_v2float_uint_4 %48 3
-// OpStore %out_var_TEXCOORD %58
-// %61 = OpCompositeExtract %_arr_v3float_uint_4 %48 4
-// OpStore %out_var_TANUCORNER %61
-// %63 = OpCompositeExtract %_arr_v3float_uint_4 %48 5
-// OpStore %out_var_TANVCORNER %63
-// %65 = OpCompositeExtract %v4float %48 6
-// OpStore %out_var_TANWEIGHTS %65
+// %75 = OpFunctionCall %HS_CONSTANT_DATA_OUTPUT %SubDToBezierConstantsHS %param_var_ip %param_var_PatchID
+// %76 = OpCompositeExtract %_arr_float_uint_4 %75 0
+// OpStore %gl_TessLevelOuter %76
+// %79 = OpCompositeExtract %_arr_float_uint_2 %75 1
+// OpStore %gl_TessLevelInner %79
+// %82 = OpCompositeExtract %_arr_v3float_uint_4 %75 2
+// OpStore %out_var_TANGENT %82
+// %85 = OpCompositeExtract %_arr_v2float_uint_4 %75 3
+// OpStore %out_var_TEXCOORD %85
+// %88 = OpCompositeExtract %_arr_v3float_uint_4 %75 4
+// OpStore %out_var_TANUCORNER %88
+// %90 = OpCompositeExtract %_arr_v3float_uint_4 %75 5
+// OpStore %out_var_TANVCORNER %90
+// %92 = OpCompositeExtract %v4float %75 6
+// OpStore %out_var_TANWEIGHTS %92
 // OpBranch %if_merge
 // %if_merge = OpLabel
 // OpReturn
 // OpFunctionEnd
-// %SubDToBezierConstantsHS = OpFunction %HS_CONSTANT_DATA_OUTPUT None %68
-// %ip = OpFunctionParameter %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16
+// %SubDToBezierConstantsHS = OpFunction %HS_CONSTANT_DATA_OUTPUT None %95
+// %ip = OpFunctionParameter %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_3
 // %PatchID = OpFunctionParameter %_ptr_Function_uint
 // %bb_entry = OpLabel
 // %Output = OpVariable %_ptr_Function_HS_CONSTANT_DATA_OUTPUT Function
-// %78 = OpAccessChain %_ptr_Function_float %Output %int_0 %int_0
-// OpStore %78 %float_1
-// %81 = OpAccessChain %_ptr_Function_float %Output %int_0 %int_1
-// OpStore %81 %float_2
-// %84 = OpAccessChain %_ptr_Function_float %Output %int_0 %int_2
-// OpStore %84 %float_3
-// %87 = OpAccessChain %_ptr_Function_float %Output %int_0 %int_3
-// OpStore %87 %float_4
-// %89 = OpAccessChain %_ptr_Function_float %Output %int_1 %int_0
-// OpStore %89 %float_5
-// %91 = OpAccessChain %_ptr_Function_float %Output %int_1 %int_1
-// OpStore %91 %float_6
-// %92 = OpLoad %HS_CONSTANT_DATA_OUTPUT %Output
-// OpReturnValue %92
+// %105 = OpAccessChain %_ptr_Function_float %Output %int_0 %int_0
+// OpStore %105 %float_1
+// %108 = OpAccessChain %_ptr_Function_float %Output %int_0 %int_1
+// OpStore %108 %float_2
+// %111 = OpAccessChain %_ptr_Function_float %Output %int_0 %int_2
+// OpStore %111 %float_3
+// %114 = OpAccessChain %_ptr_Function_float %Output %int_0 %int_3
+// OpStore %114 %float_4
+// %116 = OpAccessChain %_ptr_Function_float %Output %int_1 %int_0
+// OpStore %116 %float_5
+// %118 = OpAccessChain %_ptr_Function_float %Output %int_1 %int_1
+// OpStore %118 %float_6
+// %119 = OpLoad %HS_CONSTANT_DATA_OUTPUT %Output
+// OpReturnValue %119
 // OpFunctionEnd
-// %src_SubDToBezierHS = OpFunction %BEZIER_CONTROL_POINT None %94
-// %ip_0 = OpFunctionParameter %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16
+// %src_SubDToBezierHS = OpFunction %BEZIER_CONTROL_POINT None %121
+// %ip_0 = OpFunctionParameter %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_3
 // %cpid = OpFunctionParameter %_ptr_Function_uint
 // %PatchID_0 = OpFunctionParameter %_ptr_Function_uint
 // %bb_entry_0 = OpLabel
 // %vsOutput = OpVariable %_ptr_Function_VS_CONTROL_POINT_OUTPUT Function
 // %result = OpVariable %_ptr_Function_BEZIER_CONTROL_POINT Function
-// %104 = OpAccessChain %_ptr_Function_v3float %vsOutput %int_0
-// %105 = OpLoad %v3float %104
-// %106 = OpAccessChain %_ptr_Function_v3float %result %int_0
-// OpStore %106 %105
-// %107 = OpLoad %BEZIER_CONTROL_POINT %result
-// OpReturnValue %107
+// %131 = OpAccessChain %_ptr_Function_v3float %vsOutput %int_0
+// %132 = OpLoad %v3float %131
+// %133 = OpAccessChain %_ptr_Function_v3float %result %int_0
+// OpStore %133 %132
+// %134 = OpLoad %BEZIER_CONTROL_POINT %result
+// OpReturnValue %134
 // OpFunctionEnd

+ 0 - 70
tools/clang/test/CodeGenSPIRV/hull.output-vars.hlsl

@@ -1,70 +0,0 @@
-// Run: %dxc -T hs_6_0 -E main
-
-#include "bezier_common_hull.hlsl"
-
-// This test ensures:
-// 1- Result of the main Hull shader entry point is written to output variable.
-// 2- TessLevelOuter and TessLevelInner builtins are written to.
-// 3- All other outputs of the PCF are written to output variables.
-
-// CHECK: OpEntryPoint TessellationControl %main "main" %in_var_hullEntryPointInput %gl_InvocationID %gl_PrimitiveID %out_var_hullEntryPointOutput %gl_TessLevelOuter %gl_TessLevelInner %out_var_TANGENT %out_var_TEXCOORD %out_var_TANUCORNER %out_var_TANVCORNER %out_var_TANWEIGHTS
-
-// CHECK: OpDecorate %gl_InvocationID BuiltIn InvocationId
-// CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
-// CHECK: OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
-// CHECK: OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
-// CHECK: OpDecorate %in_var_hullEntryPointInput Location 0
-// CHECK: OpDecorate %out_var_hullEntryPointOutput Location 0
-// CHECK: OpDecorate %out_var_TANGENT Location 1
-// CHECK: OpDecorate %out_var_TEXCOORD Location 2
-// CHECK: OpDecorate %out_var_TANUCORNER Location 3
-// CHECK: OpDecorate %out_var_TANVCORNER Location 4
-// CHECK: OpDecorate %out_var_TANWEIGHTS Location 5
-
-// CHECK:   %in_var_hullEntryPointInput = OpVariable %_ptr_Input__arr_VS_CONTROL_POINT_OUTPUT_uint_16 Input
-// CHECK:              %gl_InvocationID = OpVariable %_ptr_Input_uint Input
-// CHECK:               %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input
-// CHECK: %out_var_hullEntryPointOutput = OpVariable %_ptr_Output__arr_BEZIER_CONTROL_POINT_uint_16 Output
-// CHECK:            %gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output
-// CHECK:            %gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output
-// CHECK:              %out_var_TANGENT = OpVariable %_ptr_Output__arr_v3float_uint_4 Output
-// CHECK:             %out_var_TEXCOORD = OpVariable %_ptr_Output__arr_v2float_uint_4 Output
-// CHECK:           %out_var_TANUCORNER = OpVariable %_ptr_Output__arr_v3float_uint_4 Output
-// CHECK:           %out_var_TANVCORNER = OpVariable %_ptr_Output__arr_v3float_uint_4 Output
-// CHECK:           %out_var_TANWEIGHTS = OpVariable %_ptr_Output_v4float Output
-
-// CHECK:      %param_var_ip = OpVariable %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16 Function
-// CHECK:       %param_var_i = OpVariable %_ptr_Function_uint Function
-// CHECK: %param_var_PatchID = OpVariable %_ptr_Function_uint Function
-
-// CHECK: [[mainResult:%\d+]] = OpFunctionCall %BEZIER_CONTROL_POINT %src_main %param_var_ip %param_var_i %param_var_PatchID
-// CHECK: [[mainLoc:%\d+]] = OpAccessChain %_ptr_Output_BEZIER_CONTROL_POINT %out_var_hullEntryPointOutput {{%\d+}}
-// CHECK: OpStore [[mainLoc]] [[mainResult]]
-
-// CHECK:      [[PCFResult:%\d+]] = OpFunctionCall %HS_CONSTANT_DATA_OUTPUT %SubDToBezierConstantsHS %param_var_ip %param_var_PatchID
-// CHECK-NEXT:       [[tso:%\d+]] = OpCompositeExtract %_arr_float_uint_4 [[PCFResult]] 0
-// CHECK-NEXT:                      OpStore %gl_TessLevelOuter [[tso]]
-// CHECK-NEXT:       [[tsi:%\d+]] = OpCompositeExtract %_arr_float_uint_2 [[PCFResult]] 1
-// CHECK-NEXT:                      OpStore %gl_TessLevelInner [[tsi]]
-// CHECK-NEXT:   [[tangent:%\d+]] = OpCompositeExtract %_arr_v3float_uint_4 [[PCFResult]] 2
-// CHECK-NEXT:                      OpStore %out_var_TANGENT [[tangent]]
-// CHECK-NEXT:  [[texcoord:%\d+]] = OpCompositeExtract %_arr_v2float_uint_4 [[PCFResult]] 3
-// CHECK-NEXT:                      OpStore %out_var_TEXCOORD [[texcoord]]
-// CHECK-NEXT:      [[tanu:%\d+]] = OpCompositeExtract %_arr_v3float_uint_4 [[PCFResult]] 4
-// CHECK-NEXT:                      OpStore %out_var_TANUCORNER [[tanu]]
-// CHECK-NEXT:      [[tanv:%\d+]] = OpCompositeExtract %_arr_v3float_uint_4 [[PCFResult]] 5
-// CHECK-NEXT:                      OpStore %out_var_TANVCORNER [[tanv]]
-// CHECK-NEXT:      [[tanw:%\d+]] = OpCompositeExtract %v4float [[PCFResult]] 6
-// CHECK-NEXT:                      OpStore %out_var_TANWEIGHTS [[tanw]]
-
-[domain("isoline")]
-[partitioning("fractional_odd")]
-[outputtopology("line")]
-[outputcontrolpoints(16)]
-[patchconstantfunc("SubDToBezierConstantsHS")]
-BEZIER_CONTROL_POINT main(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POINTS> ip, uint i : SV_OutputControlPointID, uint PatchID : SV_PrimitiveID) {
-  VS_CONTROL_POINT_OUTPUT vsOutput;
-  BEZIER_CONTROL_POINT result;
-  result.vPosition = vsOutput.vPosition;
-  return result;
-}

+ 0 - 5
tools/clang/test/CodeGenSPIRV/hull.pcf.input-patch.hlsl

@@ -4,15 +4,10 @@
 
 // Test: PCF takes the input control points (InputPatch)
 
-// CHECK: OpEntryPoint TessellationControl %main "main" %in_var_hullEntryPointInput {{%\w+}}
-
 // CHECK:              [[fType:%\d+]] = OpTypeFunction %HS_CONSTANT_DATA_OUTPUT %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16
-// CHECK: %in_var_hullEntryPointInput = OpVariable %_ptr_Input__arr_VS_CONTROL_POINT_OUTPUT_uint_16 Input
 
 // CHECK:                       %main = OpFunction %void None {{%\d+}}
 // CHECK:               %param_var_ip = OpVariable %_ptr_Function__arr_VS_CONTROL_POINT_OUTPUT_uint_16 Function
-// CHECK:            [[hull_in:%\d+]] = OpLoad %_arr_VS_CONTROL_POINT_OUTPUT_uint_16 %in_var_hullEntryPointInput
-// CHECK:                               OpStore %param_var_ip [[hull_in]]
 
 // CHECK:                    {{%\d+}} = OpFunctionCall %HS_CONSTANT_DATA_OUTPUT %PCF %param_var_ip
 

+ 5 - 11
tools/clang/test/CodeGenSPIRV/hull.pcf.output-patch.hlsl

@@ -5,25 +5,19 @@
 // Test: PCF takes the output (OutputPatch) of the main entry point function.
 
 
-// CHECK: OpEntryPoint TessellationControl %main "main" {{%\w+}} {{%\w+}} {{%\w+}} %out_var_hullEntryPointOutput {{%\w+}}
-
 // CHECK:               %_arr_BEZIER_CONTROL_POINT_uint_16 = OpTypeArray %BEZIER_CONTROL_POINT %uint_16
-// CHECK:   %_ptr_Output__arr_BEZIER_CONTROL_POINT_uint_16 = OpTypePointer Output %_arr_BEZIER_CONTROL_POINT_uint_16
 // CHECK: %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16 = OpTypePointer Function %_arr_BEZIER_CONTROL_POINT_uint_16
 // CHECK:                                   [[fType:%\d+]] = OpTypeFunction %HS_CONSTANT_DATA_OUTPUT %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16
-// CHECK:                    %out_var_hullEntryPointOutput = OpVariable %_ptr_Output__arr_BEZIER_CONTROL_POINT_uint_16 Output
 
-// CHECK:                             %main = OpFunction %void None {{%\d+}}
-// CHECK:    %temp_var_hullEntryPointOutput = OpVariable %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16 Function
+// CHECK:                    %main = OpFunction %void None {{%\d+}}
+// CHECK: %temp_var_hullMainRetVal = OpVariable %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16 Function
 
 // CHECK:              [[id:%\d+]] = OpLoad %uint %gl_InvocationID
 // CHECK:      [[mainResult:%\d+]] = OpFunctionCall %BEZIER_CONTROL_POINT %src_main %param_var_ip %param_var_i %param_var_PatchID
-// CHECK-NEXT:  [[outputLoc:%\d+]] = OpAccessChain %_ptr_Output_BEZIER_CONTROL_POINT %out_var_hullEntryPointOutput [[id]]
-// CHECK-NEXT:                       OpStore [[outputLoc]] [[mainResult]]
-// CHECK-NEXT:    [[tempLoc:%\d+]] = OpAccessChain %_ptr_Function_BEZIER_CONTROL_POINT %temp_var_hullEntryPointOutput [[id]]
-// CHECK-NEXT:                       OpStore [[tempLoc]] [[mainResult]]
+// CHECK:             [[loc:%\d+]] = OpAccessChain %_ptr_Function_BEZIER_CONTROL_POINT %temp_var_hullMainRetVal [[id]]
+// CHECK:                            OpStore [[loc]] [[mainResult]]
 
-// CHECK:                 {{%\d+}} = OpFunctionCall %HS_CONSTANT_DATA_OUTPUT %PCF %temp_var_hullEntryPointOutput
+// CHECK:                 {{%\d+}} = OpFunctionCall %HS_CONSTANT_DATA_OUTPUT %PCF %temp_var_hullMainRetVal
 
 // CHECK:      %PCF = OpFunction %HS_CONSTANT_DATA_OUTPUT None [[fType]]
 // CHECK-NEXT:  %op = OpFunctionParameter %_ptr_Function__arr_BEZIER_CONTROL_POINT_uint_16

+ 2 - 1
tools/clang/test/CodeGenSPIRV/hull.pcf.primitive-id.hlsl → tools/clang/test/CodeGenSPIRV/hull.pcf.primitive-id.1.hlsl

@@ -6,7 +6,8 @@
 // Note that in this test, the main entry point has also taken the PrimitiveID as input.
 
 
-// CHECK: OpEntryPoint TessellationControl %main "main" {{%\w+}} {{%\w+}} %gl_PrimitiveID {{%\w+}}
+// CHECK: OpEntryPoint TessellationControl %main "main"
+// CHECK-SAME: %gl_PrimitiveID
 
 // CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
 

+ 2 - 1
tools/clang/test/CodeGenSPIRV/hull.pcf.primitive-id-2.hlsl → tools/clang/test/CodeGenSPIRV/hull.pcf.primitive-id.2.hlsl

@@ -6,7 +6,8 @@
 // Note that in this test, the main entry point *DOES NOT* take the PrimitiveID as input.
 
 
-// CHECK: OpEntryPoint TessellationControl %main "main" {{%\w+}} {{%\w+}} {{%\w+}} %gl_PrimitiveID {{%\w+}}
+// CHECK: OpEntryPoint TessellationControl %main "main"
+// CHECK-SAME: %gl_PrimitiveID
 
 // CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
 

+ 1 - 4
tools/clang/test/CodeGenSPIRV/hull.structure.hlsl

@@ -10,10 +10,7 @@
 // CHECK:         %main = OpFunction %void None {{%\d+}}
 // CHECK:   [[id:%\d+]] = OpLoad %uint %gl_InvocationID
 
-// CHECK:  [[hullOutput:%\d+]] = OpFunctionCall %BEZIER_CONTROL_POINT %src_main %param_var_ip %param_var_i %param_var_PatchID
-
-// CHECK: [[outLocation:%\d+]] = OpAccessChain %_ptr_Output_BEZIER_CONTROL_POINT %out_var_hullEntryPointOutput [[id]]
-// CHECK:                        OpStore [[outLocation]] [[hullOutput]]
+// CHECK:      {{%\d+}} = OpFunctionCall %BEZIER_CONTROL_POINT %src_main %param_var_ip %param_var_i %param_var_PatchID
 
 // CHECK:                 OpControlBarrier %uint_2 %uint_1 %uint_0
 

+ 38 - 25
tools/clang/test/CodeGenSPIRV/passthru-vs.hlsl2spv

@@ -17,14 +17,16 @@ PSInput VSmain(float4 position: POSITION, float4 color: COLOR) {
 // ; SPIR-V
 // ; Version: 1.0
 // ; Generator: Google spiregg; 0
-// ; Bound: 38
+// ; Bound: 45
 // ; Schema: 0
 // OpCapability Shader
 // OpMemoryModel Logical GLSL450
-// OpEntryPoint Vertex %VSmain "VSmain" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR
+// OpEntryPoint Vertex %VSmain "VSmain" %gl_PerVertexOut %in_var_POSITION %in_var_COLOR %out_var_COLOR
 // OpName %bb_entry "bb.entry"
 // OpName %src_VSmain "src.VSmain"
 // OpName %VSmain "VSmain"
+// OpName %type_gl_PerVertex "type.gl_PerVertex"
+// OpName %gl_PerVertexOut "gl_PerVertexOut"
 // OpName %param_var_position "param.var.position"
 // OpName %in_var_POSITION "in.var.POSITION"
 // OpName %param_var_color "param.var.color"
@@ -36,53 +38,64 @@ PSInput VSmain(float4 position: POSITION, float4 color: COLOR) {
 // OpName %position "position"
 // OpName %color "color"
 // OpName %result "result"
-// OpDecorate %gl_Position BuiltIn Position
+// OpMemberDecorate %type_gl_PerVertex 0 BuiltIn Position
+// OpMemberDecorate %type_gl_PerVertex 1 BuiltIn PointSize
+// OpMemberDecorate %type_gl_PerVertex 2 BuiltIn ClipDistance
+// OpMemberDecorate %type_gl_PerVertex 3 BuiltIn CullDistance
+// OpDecorate %type_gl_PerVertex Block
 // OpDecorate %in_var_POSITION Location 0
 // OpDecorate %in_var_COLOR Location 1
 // OpDecorate %out_var_COLOR Location 0
+// %uint = OpTypeInt 32 0
 // %int = OpTypeInt 32 1
 // %void = OpTypeVoid
 // %3 = OpTypeFunction %void
 // %float = OpTypeFloat 32
 // %v4float = OpTypeVector %float 4
+// %uint_1 = OpConstant %uint 1
+// %_arr_float_uint_1 = OpTypeArray %float %uint_1
+// %type_gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+// %_ptr_Output_type_gl_PerVertex = OpTypePointer Output %type_gl_PerVertex
 // %_ptr_Function_v4float = OpTypePointer Function %v4float
 // %_ptr_Input_v4float = OpTypePointer Input %v4float
 // %PSInput = OpTypeStruct %v4float %v4float
 // %_ptr_Output_v4float = OpTypePointer Output %v4float
-// %23 = OpTypeFunction %PSInput %_ptr_Function_v4float %_ptr_Function_v4float
+// %30 = OpTypeFunction %PSInput %_ptr_Function_v4float %_ptr_Function_v4float
 // %_ptr_Function_PSInput = OpTypePointer Function %PSInput
+// %uint_0 = OpConstant %uint 0
 // %int_0 = OpConstant %int 0
 // %int_1 = OpConstant %int 1
+// %gl_PerVertexOut = OpVariable %_ptr_Output_type_gl_PerVertex Output
 // %in_var_POSITION = OpVariable %_ptr_Input_v4float Input
 // %in_var_COLOR = OpVariable %_ptr_Input_v4float Input
-// %gl_Position = OpVariable %_ptr_Output_v4float Output
 // %out_var_COLOR = OpVariable %_ptr_Output_v4float Output
 // %VSmain = OpFunction %void None %3
-// %5 = OpLabel
+// %13 = OpLabel
 // %param_var_position = OpVariable %_ptr_Function_v4float Function
 // %param_var_color = OpVariable %_ptr_Function_v4float Function
-// %12 = OpLoad %v4float %in_var_POSITION
-// OpStore %param_var_position %12
-// %15 = OpLoad %v4float %in_var_COLOR
-// OpStore %param_var_color %15
-// %17 = OpFunctionCall %PSInput %src_VSmain %param_var_position %param_var_color
-// %18 = OpCompositeExtract %v4float %17 0
-// OpStore %gl_Position %18
-// %21 = OpCompositeExtract %v4float %17 1
-// OpStore %out_var_COLOR %21
+// %18 = OpLoad %v4float %in_var_POSITION
+// OpStore %param_var_position %18
+// %21 = OpLoad %v4float %in_var_COLOR
+// OpStore %param_var_color %21
+// %23 = OpFunctionCall %PSInput %src_VSmain %param_var_position %param_var_color
+// %24 = OpCompositeExtract %v4float %23 0
+// %27 = OpAccessChain %_ptr_Output_v4float %gl_PerVertexOut %uint_0
+// OpStore %27 %24
+// %28 = OpCompositeExtract %v4float %23 1
+// OpStore %out_var_COLOR %28
 // OpReturn
 // OpFunctionEnd
-// %src_VSmain = OpFunction %PSInput None %23
+// %src_VSmain = OpFunction %PSInput None %30
 // %position = OpFunctionParameter %_ptr_Function_v4float
 // %color = OpFunctionParameter %_ptr_Function_v4float
 // %bb_entry = OpLabel
 // %result = OpVariable %_ptr_Function_PSInput Function
-// %29 = OpLoad %v4float %position
-// %32 = OpAccessChain %_ptr_Function_v4float %result %int_0
-// OpStore %32 %29
-// %33 = OpLoad %v4float %color
-// %35 = OpAccessChain %_ptr_Function_v4float %result %int_1
-// OpStore %35 %33
-// %36 = OpLoad %PSInput %result
-// OpReturnValue %36
-// OpFunctionEnd
+// %36 = OpLoad %v4float %position
+// %39 = OpAccessChain %_ptr_Function_v4float %result %int_0
+// OpStore %39 %36
+// %40 = OpLoad %v4float %color
+// %42 = OpAccessChain %_ptr_Function_v4float %result %int_1
+// OpStore %42 %40
+// %43 = OpLoad %PSInput %result
+// OpReturnValue %43
+// OpFunctionEnd

+ 1 - 1
tools/clang/test/CodeGenSPIRV/semantic.arbitrary.hlsl

@@ -1,6 +1,6 @@
 // Run: %dxc -T vs_6_0 -E main
 
-// CHECK: OpEntryPoint Vertex %main "main" %in_var_AAA %in_var_B %in_var_CC %out_var_DDDD
+// CHECK: OpEntryPoint Vertex %main "main" %gl_PerVertexOut %in_var_AAA %in_var_B %in_var_CC %out_var_DDDD
 
 // CHECK: OpDecorate %in_var_AAA Location 0
 // CHECK: OpDecorate %in_var_B Location 1

+ 3 - 2
tools/clang/test/CodeGenSPIRV/semantic.domain-location.ds.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T ds_6_0 -E BezierEvalDS
 
-// CHECK: OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS" {{%\w+}} {{%\w+}} %gl_TessCoord {{%\w+}} {{%\w+}}
+// CHECK: OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS"
+// CHECK-SAME: %gl_TessCoord
 
 // CHECK: OpDecorate %gl_TessCoord BuiltIn TessCoord
 // CHECK: OpDecorate %gl_TessCoord Patch
@@ -36,7 +37,7 @@ struct DS_OUTPUT
 };
 
 [domain("quad")]
-DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input, 
+DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
                         float2 UV : SV_DomainLocation,
                         const OutputPatch<BEZIER_CONTROL_POINT, 16> bezpatch )
 {

+ 3 - 2
tools/clang/test/CodeGenSPIRV/semantic.inside-tess-factor.ds.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T ds_6_0 -E BezierEvalDS
 
-// CHECK: OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS" {{%\w+}} %gl_TessLevelInner {{%\w+}} {{%\w+}} {{%\w+}}
+// CHECK: OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS"
+// CHECK-SAME: %gl_TessLevelInner
 
 // CHECK: OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
 // CHECK: OpDecorate %gl_TessLevelInner Patch
@@ -27,7 +28,7 @@ struct DS_OUTPUT
 };
 
 [domain("quad")]
-DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input, 
+DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
                         float2 UV : SV_DomainLocation,
                         const OutputPatch<BEZIER_CONTROL_POINT, 16> bezpatch )
 {

+ 2 - 1
tools/clang/test/CodeGenSPIRV/semantic.inside-tess-factor.hs.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T hs_6_0 -E SubDToBezierHS
 
-// CHECK: OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS" {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} %gl_TessLevelInner {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+// CHECK: OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS"
+// CHECK-SAME: %gl_TessLevelInner
 
 // CHECK: OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
 // CHECK: OpDecorate %gl_TessLevelInner Patch

+ 2 - 1
tools/clang/test/CodeGenSPIRV/semantic.instance-id.vs.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T vs_6_0 -E main
 
-// CHECK:                     OpEntryPoint Vertex %main "main" %gl_InstanceIndex %out_var_SV_InstanceID
+// CHECK:                     OpEntryPoint Vertex %main "main"
+// CHECK-SAME:                %gl_InstanceIndex
 
 // CHECK:                     OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
 // CHECK:                     OpDecorate %out_var_SV_InstanceID Location 0

+ 2 - 1
tools/clang/test/CodeGenSPIRV/semantic.output-control-point-id.hs.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T hs_6_0 -E SubDToBezierHS
 
-// CHECK: OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS" {{%\w+}} %gl_InvocationID {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+// CHECK: OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS"
+// CHECK-SAME: %gl_InvocationID
 
 // CHECK: OpDecorate %gl_InvocationID BuiltIn InvocationId
 

+ 0 - 35
tools/clang/test/CodeGenSPIRV/semantic.position.ds.hlsl

@@ -1,35 +0,0 @@
-// Run: %dxc -T ds_6_0 -E BezierEvalDS
-
-// CHECK: OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS" {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} %gl_Position
-
-// CHECK: OpDecorate %gl_Position BuiltIn Position
-
-// CHECK: %gl_Position = OpVariable %_ptr_Output_v4float Output
-
-
-struct HS_CONSTANT_DATA_OUTPUT
-{
-  float Edges[4]        : SV_TessFactor;
-  float Inside[2]       : SV_InsideTessFactor;
-};
-
-// Output control point (output of hull shader)
-struct BEZIER_CONTROL_POINT
-{
-  float3 vPosition	: BEZIERPOS;
-};
-
-// The domain shader outputs
-struct DS_OUTPUT
-{
-  float4 vPosition  : SV_POSITION;
-};
-
-[domain("quad")]
-DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input, 
-                        float2 UV : SV_DomainLocation,
-                        const OutputPatch<BEZIER_CONTROL_POINT, 16> bezpatch )
-{
-  DS_OUTPUT Output;
-  return Output;
-}

+ 0 - 21
tools/clang/test/CodeGenSPIRV/semantic.position.gs.hlsl

@@ -1,21 +0,0 @@
-// Run: %dxc -T gs_6_0 -E main
-
-// TODO: 'Position' semantics for GSVIn.
-
-// CHECK: OpEntryPoint Geometry %main "main" {{%\w+}} %gl_Position {{%\w+}} {{%\w+}}
-
-// CHECK: OpDecorate %gl_Position BuiltIn Position
-
-// CHECK: %gl_Position = OpVariable %_ptr_Output_v4float Output
-
-struct GS_OUT
-{
-  float4 position : SV_POSITION;
-  float4 color    : COLOR0;
-  float2 uv       : TEXCOORD0;
-};
-
-[maxvertexcount(3)] 
-void main(triangle float4 vid[3] : VertexID, inout TriangleStream <GS_OUT> outstream)
-{
-}

+ 0 - 11
tools/clang/test/CodeGenSPIRV/semantic.position.ps.hlsl

@@ -1,11 +0,0 @@
-// Run: %dxc -T ps_6_0 -E main
-
-// CHECK:                 OpEntryPoint Fragment %main "main" %gl_FragCoord %out_var_SV_Target
-
-// CHECK:                 OpDecorate %gl_FragCoord BuiltIn FragCoord
-
-// CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
-
-float4 main(float4 input: SV_Position) : SV_Target {
-    return input;
-}

+ 0 - 13
tools/clang/test/CodeGenSPIRV/semantic.position.vs.hlsl

@@ -1,13 +0,0 @@
-// Run: %dxc -T vs_6_0 -E main
-
-// CHECK:                OpEntryPoint Vertex %main "main" %in_var_SV_Position %gl_Position
-
-// CHECK:                OpDecorate %gl_Position BuiltIn Position
-// CHECK:                OpDecorate %in_var_SV_Position Location 0
-
-// CHECK: %in_var_SV_Position = OpVariable %_ptr_Input_v4float Input
-// CHECK: %gl_Position = OpVariable %_ptr_Output_v4float Output
-
-float4 main(float4 input: SV_Position) : SV_Position {
-    return input;
-}

+ 2 - 1
tools/clang/test/CodeGenSPIRV/semantic.primitive-id.hs.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T hs_6_0 -E SubDToBezierHS
 
-// CHECK: OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS" {{%\w+}} {{%\w+}} %gl_PrimitiveID {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+// CHECK: OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS"
+// CHECK-SAME: %gl_PrimitiveID
 
 // CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
 

+ 3 - 2
tools/clang/test/CodeGenSPIRV/semantic.tess-factor.ds.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T ds_6_0 -E BezierEvalDS
 
-// CHECK: OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS" %gl_TessLevelOuter {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+// CHECK: OpEntryPoint TessellationEvaluation %BezierEvalDS "BezierEvalDS"
+// CHECK-SAME: %gl_TessLevelOuter
 
 // CHECK: OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
 // CHECK: OpDecorate %gl_TessLevelOuter Patch
@@ -27,7 +28,7 @@ struct DS_OUTPUT
 };
 
 [domain("quad")]
-DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input, 
+DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
                         float2 UV : SV_DomainLocation,
                         const OutputPatch<BEZIER_CONTROL_POINT, 16> bezpatch )
 {

+ 2 - 1
tools/clang/test/CodeGenSPIRV/semantic.tess-factor.hs.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T hs_6_0 -E SubDToBezierHS
 
-// CHECK: OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS" {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} %gl_TessLevelOuter {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}}
+// CHECK: OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS"
+// CHECK-SAME: %gl_TessLevelOuter
 
 // CHECK: OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
 // CHECK: OpDecorate %gl_TessLevelOuter Patch

+ 2 - 1
tools/clang/test/CodeGenSPIRV/semantic.vertex-id.vs.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T vs_6_0 -E main
 
-// CHECK:                   OpEntryPoint Vertex %main "main" %gl_VertexIndex %out_var_A
+// CHECK:                   OpEntryPoint Vertex %main "main"
+// CHECK-SAME:              %gl_VertexIndex
 
 // CHECK:                   OpDecorate %gl_VertexIndex BuiltIn VertexIndex
 

+ 361 - 0
tools/clang/test/CodeGenSPIRV/spirv.interface.ds.hlsl

@@ -0,0 +1,361 @@
+// Run: %dxc -T ds_6_0 -E main
+
+// HS PCF output
+
+struct HsPcfOut {
+  float  outTessFactor[4]   : SV_TessFactor;        // Builtin TessLevelOuter
+  float  inTessFactor[2]    : SV_InsideTessFactor;  // Builtin TessLevelInner
+
+  float3 foo                : FOO;                  // Input variable
+};
+
+// Per-vertex input structs
+
+struct Inner2PerVertexIn {
+  float               clip3 : SV_ClipDistance3;     // Builtin ClipDistance
+  float4              texco : TEXCOORD;             // Input variable
+};
+
+struct InnerPerVertexIn {
+  float4              pos   : SV_Position;          // Builtin Position
+  float               clip0 : SV_ClipDistance0;     // Builtin ClipDistance
+  Inner2PerVertexIn   s;
+  float2              cull2 : SV_CullDistance2;     // Builtin CullDistance
+};
+
+struct PerVertexIn {
+  float4              cull3 : SV_CullDistance3;     // Builtin CullDistance
+  InnerPerVertexIn    s;
+  float2              bar   : BAR;                  // Input variable
+};
+
+// Per-vertex output structs
+
+struct Inner2PerVertexOut {
+  float3 foo                : FOO;                  // Output variable
+  float2 cull3              : SV_CullDistance3;     // Builtin CullDistance
+  float  clip0              : SV_ClipDistance0;     // Builtin ClipDistance
+};
+
+struct InnerPerVertexOut {
+  Inner2PerVertexOut s;
+  float2 cull4              : SV_CullDistance4;     // Builtin CullDistance
+  float4 bar                : BAR;                  // Output variable
+};
+
+struct DsOut {
+  float4 pos                : SV_Position;
+  InnerPerVertexOut s;
+};
+
+// Per-vertex    input  builtin : gl_PerVertex (Position, ClipDistance, CullDistance)
+// Per-vertex    output builtin : gl_PerVertex (Position, ClipDistance, CullDistance)
+// Per-vertex    input  variable: TEXCOORD, BAR
+// Per-vertex    output variable: FOO, BAR
+
+// Per-primitive input builtin  : TessLevelInner, TessLevelOuter, TessCoord (SV_DomainLocation)
+// Per-primitive input variable : FOO
+
+// CHECK: OpEntryPoint TessellationEvaluation %main "main" %gl_PerVertexIn %gl_PerVertexOut %in_var_TEXCOORD %in_var_BAR %gl_TessCoord %gl_TessLevelOuter %gl_TessLevelInner %in_var_FOO %out_var_FOO %out_var_BAR
+
+// CHECK: OpMemberDecorate %type_gl_PerVertex 0 BuiltIn Position
+// CHECK: OpMemberDecorate %type_gl_PerVertex 1 BuiltIn PointSize
+// CHECK: OpMemberDecorate %type_gl_PerVertex 2 BuiltIn ClipDistance
+// CHECK: OpMemberDecorate %type_gl_PerVertex 3 BuiltIn CullDistance
+// CHECK: OpDecorate %type_gl_PerVertex Block
+
+// CHECK: OpMemberDecorate %type_gl_PerVertex_0 0 BuiltIn Position
+// CHECK: OpMemberDecorate %type_gl_PerVertex_0 1 BuiltIn PointSize
+// CHECK: OpMemberDecorate %type_gl_PerVertex_0 2 BuiltIn ClipDistance
+// CHECK: OpMemberDecorate %type_gl_PerVertex_0 3 BuiltIn CullDistance
+// CHECK: OpDecorate %type_gl_PerVertex_0 Block
+
+// CHECK: OpDecorate %gl_TessCoord BuiltIn TessCoord
+// CHECK: OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
+// CHECK: OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
+// CHECK: OpDecorate %in_var_TEXCOORD Location 0
+// CHECK: OpDecorate %in_var_BAR Location 1
+// CHECK: OpDecorate %in_var_FOO Location 2
+// CHECK: OpDecorate %out_var_FOO Location 0
+// CHECK: OpDecorate %out_var_BAR Location 1
+
+// Input : clip0 + clip3 : 2 floats
+// Input : cull2 + cull3 : 6 floats
+// CHECK: %type_gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_2 %_arr_float_uint_6
+
+// Output: clip0 + clip5 : 4 floats
+// Output: cull3 + cull4 : 4 floats
+// CHECK: %type_gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_4 %_arr_float_uint_4
+
+// CHECK:    %gl_PerVertexIn = OpVariable %_ptr_Input__arr_type_gl_PerVertex_uint_3 Input
+// CHECK:   %gl_PerVertexOut = OpVariable %_ptr_Output_type_gl_PerVertex_0 Output
+// CHECK:   %in_var_TEXCOORD = OpVariable %_ptr_Input__arr_v4float_uint_3 Input
+// CHECK:        %in_var_BAR = OpVariable %_ptr_Input__arr_v2float_uint_3 Input
+// CHECK:      %gl_TessCoord = OpVariable %_ptr_Input_v3float Input
+// CHECK: %gl_TessLevelOuter = OpVariable %_ptr_Input__arr_float_uint_4 Input
+// CHECK: %gl_TessLevelInner = OpVariable %_ptr_Input__arr_float_uint_2 Input
+// CHECK:        %in_var_FOO = OpVariable %_ptr_Input_v3float Input
+// CHECK:       %out_var_FOO = OpVariable %_ptr_Output_v3float Output
+// CHECK:       %out_var_BAR = OpVariable %_ptr_Output_v4float Output
+
+[domain("quad")]
+DsOut main(    const OutputPatch<PerVertexIn, 3> patch,
+               float2   loc     : SV_DomainLocation,
+               HsPcfOut pcfData,
+           out float3   clip5   : SV_ClipDistance5)     // Builtin ClipDistance
+{
+  DsOut dsOut;
+  dsOut = (DsOut)0;
+  return dsOut;
+// Layout of input ClipDistance array:
+//   clip0: 1 floats, offset 0
+//   clip3: 1 floats, offset 1
+
+// Layout of input CullDistance array:
+//   cull2: 2 floats, offset 0
+//   cull3: 4 floats, offset 2
+
+// Layout of output ClipDistance array:
+//   clip0: 1 floats, offset 0
+//   clip5: 3 floats, offset 1
+
+// Layout of output CullDistance array:
+//   cull3: 2 floats, offset 0
+//   cull4: 2 floats, offset 2
+
+// Read gl_PerVertex[0].gl_ClipDistance and compose patch[0].cull3 (SV_CullDistance3)
+// CHECK:              [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_2
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_3
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:         [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_4
+// CHECK-NEXT:         [[val2:%\d+]] = OpLoad %float [[ptr2]]
+// CHECK-NEXT:         [[ptr3:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_5
+// CHECK-NEXT:         [[val3:%\d+]] = OpLoad %float [[ptr3]]
+// CHECK-NEXT:  [[patch0cull3:%\d+]] = OpCompositeConstruct %v4float [[val0]] [[val1]] [[val2]] [[val3]]
+
+// Read gl_PerVertex[1].gl_ClipDistance and compose patch[1].cull3 (SV_CullDistance3)
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_2
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_3
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:         [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_4
+// CHECK-NEXT:         [[val2:%\d+]] = OpLoad %float [[ptr2]]
+// CHECK-NEXT:         [[ptr3:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_5
+// CHECK-NEXT:         [[val3:%\d+]] = OpLoad %float [[ptr3]]
+// CHECK-NEXT:  [[patch1cull3:%\d+]] = OpCompositeConstruct %v4float [[val0]] [[val1]] [[val2]] [[val3]]
+
+// Read gl_PerVertex[2].gl_ClipDistance and compose patch[2].cull3 (SV_CullDistance3)
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_3 %uint_2
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_3 %uint_3
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:         [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_3 %uint_4
+// CHECK-NEXT:         [[val2:%\d+]] = OpLoad %float [[ptr2]]
+// CHECK-NEXT:         [[ptr3:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_3 %uint_5
+// CHECK-NEXT:         [[val3:%\d+]] = OpLoad %float [[ptr3]]
+// CHECK-NEXT:  [[patch2cull3:%\d+]] = OpCompositeConstruct %v4float [[val0]] [[val1]] [[val2]] [[val3]]
+
+// Compose an array of input SV_CullDistance3 for later use
+// CHECK-NEXT:   [[inCull3Arr:%\d+]] = OpCompositeConstruct %_arr_v4float_uint_3 [[patch0cull3]] [[patch1cull3]] [[patch2cull3]]
+
+// Read gl_PerVertex[0].gl_Position as patch[0].s.pos (SV_Position)
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_v4float %gl_PerVertexIn %uint_0 %uint_0
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %v4float [[ptr0]]
+
+// Read gl_PerVertex[1].gl_Position as patch[1].s.pos (SV_Position)
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_v4float %gl_PerVertexIn %uint_1 %uint_0
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %v4float [[ptr1]]
+
+// Read gl_PerVertex[2].gl_Position as patch[2].s.pos (SV_Position)
+// CHECK-NEXT:         [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_v4float %gl_PerVertexIn %uint_2 %uint_0
+// CHECK-NEXT:         [[val2:%\d+]] = OpLoad %v4float [[ptr2]]
+
+// Compose an array of input SV_Position for later use
+// CHECK-NEXT:     [[inPosArr:%\d+]] = OpCompositeConstruct %_arr_v4float_uint_3 [[val0]] [[val1]] [[val2]]
+
+// Read gl_PerVertex[0].gl_ClipDistance as patch[0].s.clip0 (SV_ClipDistance0)
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_2 %uint_0
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %float [[ptr0]]
+
+// Read gl_PerVertex[1].gl_ClipDistance as patch[1].s.clip0 (SV_ClipDistance0)
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_2 %uint_0
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %float [[ptr1]]
+
+// Read gl_PerVertex[2].gl_ClipDistance as patch[2].s.clip0 (SV_ClipDistance0)
+// CHECK-NEXT:         [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_2 %uint_0
+// CHECK-NEXT:         [[val2:%\d+]] = OpLoad %float [[ptr2]]
+
+// Compose an array of input SV_ClipDistance0 for later use
+// CHECK-NEXT:   [[inClip0Arr:%\d+]] = OpCompositeConstruct %_arr_float_uint_3 [[val0]] [[val1]] [[val2]]
+
+// Read gl_PerVertex[0].gl_ClipDistance as patch[0].s.s.clip3 (SV_ClipDistance3)
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_2 %uint_1
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %float [[ptr0]]
+
+// Read gl_PerVertex[1].gl_ClipDistance as patch[1].s.s.clip3 (SV_ClipDistance3)
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_2 %uint_1
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %float [[ptr1]]
+
+// Read gl_PerVertex[2].gl_ClipDistance as patch[2].s.s.clip3 (SV_ClipDistance3)
+// CHECK-NEXT:         [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_2 %uint_1
+// CHECK-NEXT:         [[val2:%\d+]] = OpLoad %float [[ptr2]]
+
+// Compose an array of input SV_ClipDistance3 for later use
+// CHECK-NEXT:   [[inClip3Arr:%\d+]] = OpCompositeConstruct %_arr_float_uint_3 [[val0]] [[val1]] [[val2]]
+
+// CHECK-NEXT:      [[texcord:%\d+]] = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD
+
+// Decompose temporary arrays created before to compose Inner2PerVertexIn
+
+// CHECK-NEXT:  [[inClip3Arr0:%\d+]] = OpCompositeExtract %float [[inClip3Arr]] 0
+// CHECK-NEXT:     [[texcord0:%\d+]] = OpCompositeExtract %v4float [[texcord]] 0
+// CHECK-NEXT:         [[val0:%\d+]] = OpCompositeConstruct %Inner2PerVertexIn [[inClip3Arr0]] [[texcord0]]
+
+// CHECK-NEXT:  [[inClip3Arr1:%\d+]] = OpCompositeExtract %float [[inClip3Arr]] 1
+// CHECK-NEXT:     [[texcord1:%\d+]] = OpCompositeExtract %v4float [[texcord]] 1
+// CHECK-NEXT:         [[val1:%\d+]] = OpCompositeConstruct %Inner2PerVertexIn [[inClip3Arr1]] [[texcord1]]
+
+// CHECK-NEXT:  [[inClip3Arr2:%\d+]] = OpCompositeExtract %float [[inClip3Arr]] 2
+// CHECK-NEXT:     [[texcord2:%\d+]] = OpCompositeExtract %v4float [[texcord]] 2
+// CHECK-NEXT:         [[val2:%\d+]] = OpCompositeConstruct %Inner2PerVertexIn [[inClip3Arr2]] [[texcord2]]
+
+// Compose an array of input Inner2PerVertexIn for later use
+// CHECK-NEXT:   [[inIn2PVArr:%\d+]] = OpCompositeConstruct %_arr_Inner2PerVertexIn_uint_3 [[val0]] [[val1]] [[val2]]
+
+// Read gl_PerVertex[0].gl_CullDistance as patch[0].s.cull2 (SV_CullDistance2)
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_0
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_1
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:  [[patch0cull2:%\d+]] = OpCompositeConstruct %v2float [[val0]] [[val1]]
+
+// Read gl_PerVertex[1].gl_CullDistance as patch[1].s.cull2 (SV_CullDistance2)
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_0
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_1
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:  [[patch1cull2:%\d+]] = OpCompositeConstruct %v2float [[val0]] [[val1]]
+
+// Read gl_PerVertex[2].gl_CullDistance as patch[2].s.cull2 (SV_CullDistance2)
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_3 %uint_0
+// CHECK-NEXT:         [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_2 %uint_3 %uint_1
+// CHECK-NEXT:         [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:  [[patch2cull2:%\d+]] = OpCompositeConstruct %v2float [[val0]] [[val1]]
+
+// Compose an array of input SV_CullDistance2 for later use
+// CHECK-NEXT:   [[inCull2Arr:%\d+]] = OpCompositeConstruct %_arr_v2float_uint_3 [[patch0cull2]] [[patch1cull2]] [[patch2cull2]]
+
+// Decompose temporary arrays created before to compose InnerPerVertexIn
+
+// CHECK-NEXT:       [[field0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 0
+// CHECK-NEXT:       [[field1:%\d+]] = OpCompositeExtract %float [[inClip0Arr]] 0
+// CHECK-NEXT:       [[field2:%\d+]] = OpCompositeExtract %Inner2PerVertexIn [[inIn2PVArr]] 0
+// CHECK-NEXT:       [[field3:%\d+]] = OpCompositeExtract %v2float [[inCull2Arr]] 0
+// CHECK-NEXT:         [[val0:%\d+]] = OpCompositeConstruct %InnerPerVertexIn [[field0]] [[field1]] [[field2]] [[field3]]
+
+// CHECK-NEXT:       [[field0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 1
+// CHECK-NEXT:       [[field1:%\d+]] = OpCompositeExtract %float [[inClip0Arr]] 1
+// CHECK-NEXT:       [[field2:%\d+]] = OpCompositeExtract %Inner2PerVertexIn [[inIn2PVArr]] 1
+// CHECK-NEXT:       [[field3:%\d+]] = OpCompositeExtract %v2float [[inCull2Arr]] 1
+// CHECK-NEXT:         [[val1:%\d+]] = OpCompositeConstruct %InnerPerVertexIn [[field0]] [[field1]] [[field2]] [[field3]]
+
+// CHECK-NEXT:       [[field0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 2
+// CHECK-NEXT:       [[field1:%\d+]] = OpCompositeExtract %float [[inClip0Arr]] 2
+// CHECK-NEXT:       [[field2:%\d+]] = OpCompositeExtract %Inner2PerVertexIn [[inIn2PVArr]] 2
+// CHECK-NEXT:       [[field3:%\d+]] = OpCompositeExtract %v2float [[inCull2Arr]] 2
+// CHECK-NEXT:         [[val2:%\d+]] = OpCompositeConstruct %InnerPerVertexIn [[field0]] [[field1]] [[field2]] [[field3]]
+
+// Compose an array of input InnerPerVertexIn for later use
+// CHECK-NEXT:    [[inInPVArr:%\d+]] = OpCompositeConstruct %_arr_InnerPerVertexIn_uint_3 [[val0]] [[val1]] [[val2]]
+
+// CHECK-NEXT:     [[inBarArr:%\d+]] = OpLoad %_arr_v2float_uint_3 %in_var_BAR
+
+// Decompose temporary arrays created before to compose PerVertexIn
+
+// CHECK-NEXT:       [[field0:%\d+]] = OpCompositeExtract %v4float [[inCull3Arr]] 0
+// CHECK-NEXT:       [[field1:%\d+]] = OpCompositeExtract %InnerPerVertexIn [[inInPVArr]] 0
+// CHECK-NEXT:       [[field2:%\d+]] = OpCompositeExtract %v2float [[inBarArr]] 0
+// CHECK-NEXT:         [[val0:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]]
+
+// CHECK-NEXT:       [[field0:%\d+]] = OpCompositeExtract %v4float [[inCull3Arr]] 1
+// CHECK-NEXT:       [[field1:%\d+]] = OpCompositeExtract %InnerPerVertexIn [[inInPVArr]] 1
+// CHECK-NEXT:       [[field2:%\d+]] = OpCompositeExtract %v2float [[inBarArr]] 1
+// CHECK-NEXT:         [[val1:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]]
+
+// CHECK-NEXT:       [[field0:%\d+]] = OpCompositeExtract %v4float [[inCull3Arr]] 2
+// CHECK-NEXT:       [[field1:%\d+]] = OpCompositeExtract %InnerPerVertexIn [[inInPVArr]] 2
+// CHECK-NEXT:       [[field2:%\d+]] = OpCompositeExtract %v2float [[inBarArr]] 2
+// CHECK-NEXT:         [[val2:%\d+]] = OpCompositeConstruct %PerVertexIn [[field0]] [[field1]] [[field2]]
+
+// The final value for the patch parameter!
+// CHECK-NEXT:        [[patch:%\d+]] = OpCompositeConstruct %_arr_PerVertexIn_uint_3 [[val0]] [[val1]] [[val2]]
+// CHECK-NEXT:                         OpStore %param_var_patch [[patch]]
+
+// Write SV_DomainLocation to tempoary variable for function call
+// CHECK-NEXT:    [[tesscoord:%\d+]] = OpLoad %v3float %gl_TessCoord
+// CHECK-NEXT:      [[shuffle:%\d+]] = OpVectorShuffle %v2float [[tesscoord]] [[tesscoord]] 0 1
+// CHECK-NEXT:                         OpStore %param_var_loc [[shuffle]]
+
+// Compose pcfData and write to tempoary variable for function call
+// CHECK-NEXT:          [[tlo:%\d+]] = OpLoad %_arr_float_uint_4 %gl_TessLevelOuter
+// CHECK-NEXT:          [[tli:%\d+]] = OpLoad %_arr_float_uint_2 %gl_TessLevelInner
+// CHECK-NEXT:        [[inFoo:%\d+]] = OpLoad %v3float %in_var_FOO
+// CHECK-NEXT:      [[pcfData:%\d+]] = OpCompositeConstruct %HsPcfOut [[tlo]] [[tli]] [[inFoo]]
+// CHECK-NEXT:                         OpStore %param_var_pcfData [[pcfData]]
+
+// Make the call!
+// CHECK-NEXT:          [[ret:%\d+]] = OpFunctionCall %DsOut %src_main %param_var_patch %param_var_loc %param_var_pcfData %param_var_clip5
+
+// Decompose DsOut and write out output SV_Position
+// CHECK-NEXT:       [[outPos:%\d+]] = OpCompositeExtract %v4float [[ret]] 0
+// CHECK-NEXT:          [[ptr:%\d+]] = OpAccessChain %_ptr_Output_v4float %gl_PerVertexOut %uint_0
+// CHECK-NEXT:                         OpStore [[ptr]] [[outPos]]
+
+// CHECK-NEXT:      [[outInPV:%\d+]] = OpCompositeExtract %InnerPerVertexOut [[ret]] 1
+// CHECK-NEXT:     [[outIn2PV:%\d+]] = OpCompositeExtract %Inner2PerVertexOut [[outInPV]] 0
+
+// Decompose Inner2PerVertexOut and write out DsOut.s.s.foo (FOO)
+// CHECK-NEXT:          [[foo:%\d+]] = OpCompositeExtract %v3float [[outIn2PV]] 0
+// CHECK-NEXT:                         OpStore %out_var_FOO [[foo]]
+
+// Decompose Inner2PerVertexOut and write out DsOut.s.s.cull3 (SV_CullDistance3) at offset 0
+// CHECK-NEXT:        [[cull3:%\d+]] = OpCompositeExtract %v2float [[outIn2PV]] 1
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_3 %uint_0
+// CHECK-NEXT:         [[val0:%\d+]] = OpCompositeExtract %float [[cull3]] 0
+// CHECK-NEXT:                         OpStore [[ptr0]] [[val0]]
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_3 %uint_1
+// CHECK-NEXT:         [[val1:%\d+]] = OpCompositeExtract %float [[cull3]] 1
+// CHECK-NEXT:                         OpStore [[ptr1]] [[val1]]
+
+// Decompose Inner2PerVertexOut and write out DsOut.s.s.clip0 (SV_ClipDistance0) at offset 0
+// CHECK-NEXT:        [[clip0:%\d+]] = OpCompositeExtract %float [[outIn2PV]] 2
+// CHECK-NEXT:          [[ptr:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_0
+// CHECK-NEXT:                         OpStore [[ptr]] [[clip0]]
+
+// Decompose InnerPerVertexOut and write out DsOut.s.cull4 (SV_CullDistance4) at offset 2
+// CHECK-NEXT:        [[cull4:%\d+]] = OpCompositeExtract %v2float [[outInPV]] 1
+// CHECK-NEXT:         [[ptr0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_3 %uint_2
+// CHECK-NEXT:         [[val0:%\d+]] = OpCompositeExtract %float [[cull4]] 0
+// CHECK-NEXT:                         OpStore [[ptr0]] [[val0]]
+// CHECK-NEXT:         [[ptr1:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_3 %uint_3
+// CHECK-NEXT:         [[val1:%\d+]] = OpCompositeExtract %float [[cull4]] 1
+// CHECK-NEXT:                         OpStore [[ptr1]] [[val1]]
+
+// Decompose InnerPerVertexOut and write out DsOut.s.bar (BAR)
+// CHECK-NEXT:          [[bar:%\d+]] = OpCompositeExtract %v4float [[outInPV]] 2
+// CHECK-NEXT:                         OpStore %out_var_BAR [[bar]]
+
+// Write out clip5 (SV_ClipDistance5) at offset 1
+// CHECK-NEXT: [[clip5:%\d+]] = OpLoad %v3float %param_var_clip5
+// CHECK-NEXT:  [[ptr0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_1
+// CHECK-NEXT:  [[val0:%\d+]] = OpCompositeExtract %float [[clip5]] 0
+// CHECK-NEXT:                  OpStore [[ptr0]] [[val0]]
+// CHECK-NEXT:  [[ptr1:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_2
+// CHECK-NEXT:  [[val1:%\d+]] = OpCompositeExtract %float [[clip5]] 1
+// CHECK-NEXT:                  OpStore [[ptr1]] [[val1]]
+// CHECK-NEXT:  [[ptr2:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_3
+// CHECK-NEXT:  [[val2:%\d+]] = OpCompositeExtract %float [[clip5]] 2
+// CHECK-NEXT:                  OpStore [[ptr2]] [[val2]]
+}

+ 298 - 0
tools/clang/test/CodeGenSPIRV/spirv.interface.hs.hlsl

@@ -0,0 +1,298 @@
+// Run: %dxc -T hs_6_0 -E main
+
+// TODO: Handle the Patch decoration
+
+#define NumOutPoints 2
+
+// Input control point
+struct HsCpIn
+{
+    float4 pos     : SV_Position;      // Builtin Position
+    float2 clip0   : SV_ClipDistance0; // Builtin ClipDistance
+    float  cull5   : SV_CullDistance5; // Builtin CullDistance
+    float1 clip2   : SV_ClipDistance2; // Builtin ClipDistance
+    float3 cull3   : SV_CullDistance3; // Builtin CullDistance
+
+    float3 baz     : BAZ;              // Input variable
+};
+
+struct CpInner2 {
+    float1 clip8   : SV_ClipDistance8; // Builtin ClipDistance
+    float2 cull6   : SV_CullDistance6; // Builtin CullDistance
+    float3 foo     : FOO;              // Output variable
+};
+
+struct CpInner1 {
+    float4   pos   : SV_Position;      // Builtin Position
+    CpInner2 s;
+    float    clip6 : SV_ClipDistance6; // Builtin ClipDistance
+    float4   bar   : BAR;              // Output variable
+};
+
+// Output control point
+struct HsCpOut
+{
+    float3   cull9 : SV_CullDistance9; // Builtin CullDistance
+    CpInner1 s;
+    float1   clip7 : SV_ClipDistance7; // Builtin ClipDistance
+};
+
+// Output patch constant data.
+struct HsPcfOut
+{
+  float tessOuter[4] : SV_TessFactor;
+  float tessInner[2] : SV_InsideTessFactor;
+
+  float2 texCoord[4] : TEXCOORD;
+  float4 weight      : WEIGHT;
+};
+
+// Per-vertex    input  builtin : gl_PerVertex (Position, ClipDistance, CullDistance), InvocationID
+// Per-vertex    output builtin : gl_PerVertex (Position, ClipDistance, CullDistance)
+// Per-vertex    input  variable: BAZ
+// Per-vertex    output variable: FOO, BAR
+
+// Per-primitive input  builtin : PrimitiveID
+// Per-primitive output builtin : TessLevelInner, TessLevelOuter
+// Per-primitive output variable: TEXCOORD, WEIGHT
+
+// CHECK: OpEntryPoint TessellationControl %main "main" %gl_PerVertexIn %gl_PerVertexOut %in_var_BAZ %gl_InvocationID %gl_PrimitiveID %out_var_FOO %out_var_BAR %gl_TessLevelOuter %gl_TessLevelInner %out_var_TEXCOORD %out_var_WEIGHT
+
+// CHECK: OpMemberDecorate %type_gl_PerVertex 0 BuiltIn Position
+// CHECK: OpMemberDecorate %type_gl_PerVertex 1 BuiltIn PointSize
+// CHECK: OpMemberDecorate %type_gl_PerVertex 2 BuiltIn ClipDistance
+// CHECK: OpMemberDecorate %type_gl_PerVertex 3 BuiltIn CullDistance
+// CHECK: OpDecorate %type_gl_PerVertex Block
+
+// CHECK: OpMemberDecorate %type_gl_PerVertex_0 0 BuiltIn Position
+// CHECK: OpMemberDecorate %type_gl_PerVertex_0 1 BuiltIn PointSize
+// CHECK: OpMemberDecorate %type_gl_PerVertex_0 2 BuiltIn ClipDistance
+// CHECK: OpMemberDecorate %type_gl_PerVertex_0 3 BuiltIn CullDistance
+// CHECK: OpDecorate %type_gl_PerVertex_0 Block
+
+// CHECK: OpDecorate %gl_InvocationID BuiltIn InvocationId
+// CHECK: OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
+// CHECK: OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
+// CHECK: OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
+
+// CHECK: OpDecorate %in_var_BAZ Location 0
+// CHECK: OpDecorate %out_var_FOO Location 0
+// CHECK: OpDecorate %out_var_BAR Location 1
+// CHECK: OpDecorate %out_var_TEXCOORD Location 2
+// CHECK: OpDecorate %out_var_WEIGHT Location 3
+
+// Input : clip0 + clip2         : 3 floats
+// Input : cull3 + cull5         : 4 floats
+// CHECK:   %type_gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_3 %_arr_float_uint_4
+
+// Output: clip6 + clip7 + clip8 : 3 floats
+// Output: cull6 + cull9         : 5 floats
+// CHECK: %type_gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_3 %_arr_float_uint_5
+
+// CHECK:    %gl_PerVertexIn = OpVariable %_ptr_Input__arr_type_gl_PerVertex_uint_2 Input
+// CHECK:   %gl_PerVertexOut = OpVariable %_ptr_Output__arr_type_gl_PerVertex_0_uint_2 Output
+
+// CHECK:        %in_var_BAZ = OpVariable %_ptr_Input__arr_v3float_uint_2 Input
+// CHECK:   %gl_InvocationID = OpVariable %_ptr_Input_uint Input
+// CHECK:    %gl_PrimitiveID = OpVariable %_ptr_Input_uint Input
+// CHECK:       %out_var_FOO = OpVariable %_ptr_Output__arr_v3float_uint_2 Output
+// CHECK:       %out_var_BAR = OpVariable %_ptr_Output__arr_v4float_uint_2 Output
+// CHECK: %gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output
+// CHECK: %gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output
+// CHECK:  %out_var_TEXCOORD = OpVariable %_ptr_Output__arr_v2float_uint_4 Output
+// CHECK:    %out_var_WEIGHT = OpVariable %_ptr_Output_v4float Output
+
+// Patch Constant Function
+HsPcfOut pcf(InputPatch<HsCpIn, NumOutPoints> patch, uint patchId : SV_PrimitiveID) {
+  HsPcfOut output;
+  output = (HsPcfOut)0;
+  return output;
+}
+
+// Layout of input ClipDistance array:
+//   clip0: 2 floats, offset 0
+//   clip2: 1 floats, offset 2
+
+// Layout of input CullDistance array:
+//   cull3: 3 floats, offset 0
+//   cull5: 1 floats, offset 3
+
+// Layout of output ClipDistance array:
+//   clip6: 1 floats, offset 0
+//   clip7: 1 floats, offset 1
+//   clip8: 1 floats, offset 2
+
+// Layout of output CullDistance array:
+//   cull6: 2 floats, offset 0
+//   cull9: 3 floats, offset 2
+
+[domain("quad")]
+[partitioning("fractional_odd")]
+[outputtopology("triangle_ccw")]
+[outputcontrolpoints(NumOutPoints)]
+[patchconstantfunc("pcf")]
+HsCpOut main(InputPatch<HsCpIn, NumOutPoints> patch, uint cpId : SV_OutputControlPointID, uint patchId : SV_PrimitiveID) {
+    HsCpOut output;
+    output = (HsCpOut)0;
+    return output;
+
+// Read gl_PerVertex[].gl_Postion and compose a new array for HsCpIn::pos
+
+// CHECK:           [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_v4float %gl_PerVertexIn %uint_0 %uint_0
+// CHECK-NEXT:      [[val0:%\d+]] = OpLoad %v4float [[ptr0]]
+// CHECK-NEXT:      [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_v4float %gl_PerVertexIn %uint_1 %uint_0
+// CHECK-NEXT:      [[val1:%\d+]] = OpLoad %v4float [[ptr1]]
+// CHECK-NEXT:  [[inPosArr:%\d+]] = OpCompositeConstruct %_arr_v4float_uint_2 [[val0]] [[val1]]
+
+// Read gl_PerVertex[].gl_ClipDistance[] to compose a new array for HsCpIn::clip0
+
+// CHECK-NEXT:      [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_2 %uint_0
+// CHECK-NEXT:      [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:      [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_2 %uint_1
+// CHECK-NEXT:      [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:    [[clip00:%\d+]] = OpCompositeConstruct %v2float [[val0]] [[val1]]
+
+// CHECK-NEXT:      [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_2 %uint_0
+// CHECK-NEXT:      [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:      [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_2 %uint_1
+// CHECK-NEXT:      [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:    [[clip01:%\d+]] = OpCompositeConstruct %v2float [[val0]] [[val1]]
+
+// CHECK-NEXT: [[inClip0Arr:%\d+]] = OpCompositeConstruct %_arr_v2float_uint_2 [[clip00]] [[clip01]]
+
+// Read gl_PerVertex[].gl_CullDistance[] to compose a new array for HsCpIn::cull5
+
+// CHECK-NEXT:       [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_3
+// CHECK-NEXT:       [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:       [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_3
+// CHECK-NEXT:       [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT: [[inCull5Arr:%\d+]] = OpCompositeConstruct %_arr_float_uint_2 [[val0]] [[val1]]
+
+// Read gl_PerVertex[].gl_ClipDistance[] to compose a new array for HsCpIn::clip2
+
+// CHECK-NEXT:       [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_2 %uint_2
+// CHECK-NEXT:       [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:       [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_2 %uint_2
+// CHECK-NEXT:       [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT: [[inClip2Arr:%\d+]] = OpCompositeConstruct %_arr_float_uint_2 [[val0]] [[val1]]
+
+// Read gl_PerVertex[].gl_CullDistance[] to compose a new array for HsCpIn::cull3
+
+// CHECK-NEXT:       [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_0
+// CHECK-NEXT:       [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:       [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_1
+// CHECK-NEXT:       [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:       [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_0 %uint_3 %uint_2
+// CHECK-NEXT:       [[val2:%\d+]] = OpLoad %float [[ptr2]]
+// CHECK-NEXT:     [[cull30:%\d+]] = OpCompositeConstruct %v3float [[val0]] [[val1]] [[val2]]
+
+// CHECK-NEXT:       [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_0
+// CHECK-NEXT:       [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:       [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_1
+// CHECK-NEXT:       [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:       [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_PerVertexIn %uint_1 %uint_3 %uint_2
+// CHECK-NEXT:       [[val2:%\d+]] = OpLoad %float [[ptr2]]
+// CHECK-NEXT:     [[cull31:%\d+]] = OpCompositeConstruct %v3float [[val0]] [[val1]] [[val2]]
+
+// CHECK-NEXT: [[inCull3Arr:%\d+]] = OpCompositeConstruct %_arr_v3float_uint_2 [[cull30]] [[cull31]]
+
+// CHECK-NEXT:   [[inBazArr:%\d+]] = OpLoad %_arr_v3float_uint_2 %in_var_BAZ
+
+// Compose a temporary HsCpIn value out of the temporary arrays constructed before
+// CHECK-NEXT:       [[val0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 0
+// CHECK-NEXT:       [[val1:%\d+]] = OpCompositeExtract %v2float [[inClip0Arr]] 0
+// CHECK-NEXT:       [[val2:%\d+]] = OpCompositeExtract %float [[inCull5Arr]] 0
+// CHECK-NEXT:       [[val3:%\d+]] = OpCompositeExtract %float [[inClip2Arr]] 0
+// CHECK-NEXT:       [[val4:%\d+]] = OpCompositeExtract %v3float [[inCull3Arr]] 0
+// CHECK-NEXT:       [[val5:%\d+]] = OpCompositeExtract %v3float [[inBazArr]] 0
+// CHECK-NEXT:    [[hscpin0:%\d+]] = OpCompositeConstruct %HsCpIn [[val0]] [[val1]] [[val2]] [[val3]] [[val4]] [[val5]]
+
+// Compose a temporary HsCpIn value out of the temporary arrays constructed before
+// CHECK-NEXT:       [[val0:%\d+]] = OpCompositeExtract %v4float [[inPosArr]] 1
+// CHECK-NEXT:       [[val1:%\d+]] = OpCompositeExtract %v2float [[inClip0Arr]] 1
+// CHECK-NEXT:       [[val2:%\d+]] = OpCompositeExtract %float [[inCull5Arr]] 1
+// CHECK-NEXT:       [[val3:%\d+]] = OpCompositeExtract %float [[inClip2Arr]] 1
+// CHECK-NEXT:       [[val4:%\d+]] = OpCompositeExtract %v3float [[inCull3Arr]] 1
+// CHECK-NEXT:       [[val5:%\d+]] = OpCompositeExtract %v3float [[inBazArr]] 1
+// CHECK-NEXT:    [[hscpin1:%\d+]] = OpCompositeConstruct %HsCpIn [[val0]] [[val1]] [[val2]] [[val3]] [[val4]] [[val5]]
+
+// Populate the temporary variables for function call
+
+// CHECK-NEXT:      [[patch:%\d+]] = OpCompositeConstruct %_arr_HsCpIn_uint_2 [[hscpin0]] [[hscpin1]]
+// CHECK-NEXT:                       OpStore %param_var_patch [[patch]]
+
+// CHECK-NEXT:     [[invoId:%\d+]] = OpLoad %uint %gl_InvocationID
+// CHECK-NEXT:                       OpStore %param_var_cpId [[invoId]]
+
+// CHECK-NEXT:     [[primId:%\d+]] = OpLoad %uint %gl_PrimitiveID
+// CHECK-NEXT:                       OpStore %param_var_patchId [[primId]]
+
+// CHECK-NEXT:        [[ret:%\d+]] = OpFunctionCall %HsCpOut %src_main %param_var_patch %param_var_cpId %param_var_patchId
+
+// Write out HsCpOut::cull9 into gl_PerVertex[].gl_CullDistance[]
+// CHECK-NEXT:      [[cull9:%\d+]] = OpCompositeExtract %v3float [[ret]] 0
+// CHECK-NEXT:       [[ptr0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_3 %uint_2
+// CHECK-NEXT:       [[val0:%\d+]] = OpCompositeExtract %float [[cull9]] 0
+// CHECK-NEXT:                       OpStore [[ptr0]] [[val0]]
+// CHECK-NEXT:       [[ptr1:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_3 %uint_3
+// CHECK-NEXT:       [[val1:%\d+]] = OpCompositeExtract %float [[cull9]] 1
+// CHECK-NEXT:                       OpStore [[ptr1]] [[val1]]
+// CHECK-NEXT:       [[ptr2:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_3 %uint_4
+// CHECK-NEXT:       [[val2:%\d+]] = OpCompositeExtract %float [[cull9]] 2
+// CHECK-NEXT:                       OpStore [[ptr2]] [[val2]]
+
+// CHECK-NEXT:  [[outInner1:%\d+]] = OpCompositeExtract %CpInner1 [[ret]] 1
+
+// Write out HsCpOut::CpInner1::pos to gl_PerVertex[].gl_Position
+// CHECK-NEXT:     [[outPos:%\d+]] = OpCompositeExtract %v4float [[outInner1]] 0
+// CHECK-NEXT:        [[ptr:%\d+]] = OpAccessChain %_ptr_Output_v4float %gl_PerVertexOut [[invoId]] %uint_0
+// CHECK-NEXT:                      OpStore [[ptr]] [[outPos:%\d+]]
+
+// Write out HsCpOut::CpInner1::CpInner2::clip8 to gl_PerVertex[].gl_ClipDistance
+// CHECK-NEXT:  [[outInner2:%\d+]] = OpCompositeExtract %CpInner2 [[outInner1]] 1
+// CHECK-NEXT:   [[outClip8:%\d+]] = OpCompositeExtract %float [[outInner2]] 0
+// CHECK-NEXT:        [[ptr:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_2 %uint_2
+// CHECK-NEXT:                       OpStore [[ptr]] [[outClip8]]
+
+// Write out HsCpOut::CpInner1::CpInner2::cull6 to gl_PerVertex[].gl_CullDistance
+// CHECK-NEXT:   [[outCull6:%\d+]] = OpCompositeExtract %v2float [[outInner2]] 1
+// CHECK-NEXT:       [[ptr0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_3 %uint_0
+// CHECK-NEXT:       [[val0:%\d+]] = OpCompositeExtract %float [[outCull6]] 0
+// CHECK-NEXT:                       OpStore [[ptr0]] [[val0]]
+// CHECK-NEXT:       [[ptr1:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_3 %uint_1
+// CHECK-NEXT:       [[val1:%\d+]] = OpCompositeExtract %float [[outCull6]] 1
+// CHECK-NEXT:                       OpStore [[ptr1]] [[val1]]
+
+// Write out HsCpOut::CpInner1::CpInner2::foo to out_var_FOO
+// CHECK-NEXT:        [[foo:%\d+]] = OpCompositeExtract %v3float [[outInner2]] 2
+// CHECK-NEXT:        [[ptr:%\d+]] = OpAccessChain %_ptr_Output_v3float %out_var_FOO [[invoId]]
+// CHECK-NEXT:                       OpStore [[ptr]] [[foo]]
+
+// Write out HsCpOut::CpInner1::clip6 to gl_PerVertex[].gl_ClipDistance
+// CHECK-NEXT:      [[clip6:%\d+]] = OpCompositeExtract %float [[outInner1]] 2
+// CHECK-NEXT:        [[ptr:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_2 %uint_0
+// CHECK-NEXT:                       OpStore [[ptr]] [[clip6]]
+
+// Write out HsCpOut::CpInner1::bar to out_var_BAR
+// CHECK-NEXT:        [[bar:%\d+]] = OpCompositeExtract %v4float [[outInner1]] 3
+// CHECK-NEXT:        [[ptr:%\d+]] = OpAccessChain %_ptr_Output_v4float %out_var_BAR [[invoId]]
+// CHECK-NEXT:                       OpStore [[ptr]] [[bar]]
+
+// Write out HsCpOut::clip7 to gl_PerVertex[].gl_ClipDistance
+// CHECK-NEXT:      [[clip7:%\d+]] = OpCompositeExtract %float [[ret]] 2
+// CHECK-NEXT:        [[ptr:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut [[invoId]] %uint_2 %uint_1
+// CHECK-NEXT:                       OpStore [[ptr]] [[clip7]]
+
+// Call PCF
+// CHECK:             [[ret:%\d+]] = OpFunctionCall %HsPcfOut %pcf %param_var_patch %param_var_patchId
+
+// Write out PCF output
+// CHECK-NEXT:        [[tlo:%\d+]] = OpCompositeExtract %_arr_float_uint_4 [[ret]] 0
+// CHECK-NEXT:                       OpStore %gl_TessLevelOuter [[tlo]]
+// CHECK-NEXT:        [[tli:%\d+]] = OpCompositeExtract %_arr_float_uint_2 [[ret]] 1
+// CHECK-NEXT:                       OpStore %gl_TessLevelInner [[tli]]
+// CHECK-NEXT:    [[texcord:%\d+]] = OpCompositeExtract %_arr_v2float_uint_4 [[ret]] 2
+// CHECK-NEXT:                       OpStore %out_var_TEXCOORD [[texcord]]
+// CHECK-NEXT:     [[weight:%\d+]] = OpCompositeExtract %v4float [[ret]] 3
+// CHECK-NEXT:                       OpStore %out_var_WEIGHT [[weight]]
+}

+ 85 - 0
tools/clang/test/CodeGenSPIRV/spirv.interface.ps.hlsl

@@ -0,0 +1,85 @@
+// Run: %dxc -T ps_6_0 -E main
+
+struct Inner {
+    float2 cull2 : SV_CullDistance2;            // Builtin CullDistance
+    float3 foo   : FOO;                         // Input variable
+};
+
+struct PsIn {
+    float4 pos   : SV_Position;                 // Builtin FragCoord
+    float2 clip0 : SV_ClipDistance0;            // Builtin ClipDistance
+    Inner  s;
+};
+
+// CHECK: OpEntryPoint Fragment %main "main" %gl_ClipDistance %gl_CullDistance %gl_FragCoord %in_var_FOO %in_var_BAR %out_var_SV_Target
+
+// CHECK: OpDecorate %gl_ClipDistance BuiltIn ClipDistance
+// CHECK: OpDecorate %gl_CullDistance BuiltIn CullDistance
+// CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+// CHECK: OpDecorate %in_var_FOO Location 0
+// CHECK: OpDecorate %in_var_BAR Location 1
+// CHECK: OpDecorate %out_var_SV_Target Location 0
+
+// Input : clip0 + clip1 : 3 floats
+// Input : cull1 + cull2 : 5 floats
+// CHECK:   %gl_ClipDistance = OpVariable %_ptr_Input__arr_float_uint_3 Input
+// CHECK:   %gl_CullDistance = OpVariable %_ptr_Input__arr_float_uint_5 Input
+// CHECK:      %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+// CHECK:        %in_var_FOO = OpVariable %_ptr_Input_v3float Input
+// CHECK:        %in_var_BAR = OpVariable %_ptr_Input_float Input
+// CHECK: %out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+
+float4 main(   PsIn   psIn,
+               float  clip1 : SV_ClipDistance1, // Builtin ClipDistance
+               float3 cull1 : SV_CullDistance1, // Builtin CullDistance
+            in float  bar   : BAR               // Input variable
+           ) : SV_Target {                      // Output variable
+    return 1.0;
+// Layout of input ClipDistance array:
+//   clip0: 2 floats, offset 0
+//   clip1: 1 floats, offset 2
+
+// Layout of input CullDistance array:
+//   cull1: 3 floats, offset 0
+//   cull2: 2 floats, offset 3
+
+// CHECK:        [[pos:%\d+]] = OpLoad %v4float %gl_FragCoord
+
+// CHECK-NEXT:  [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_ClipDistance %uint_0
+// CHECK-NEXT:  [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:  [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_ClipDistance %uint_1
+// CHECK-NEXT:  [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT: [[clip0:%\d+]] = OpCompositeConstruct %v2float [[val0]] [[val1]]
+
+// CHECK-NEXT:  [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_CullDistance %uint_3
+// CHECK-NEXT:  [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:  [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_CullDistance %uint_4
+// CHECK-NEXT:  [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT: [[cull2:%\d+]] = OpCompositeConstruct %v2float [[val0]] [[val1]]
+
+// CHECK-NEXT:   [[foo:%\d+]] = OpLoad %v3float %in_var_FOO
+
+// CHECK-NEXT: [[inner:%\d+]] = OpCompositeConstruct %Inner [[cull2]] [[foo]]
+
+// CHECK-NEXT:  [[psin:%\d+]] = OpCompositeConstruct %PsIn [[pos]] [[clip0]] [[inner]]
+// CHECK-NEXT:                  OpStore %param_var_psIn [[psin]]
+
+// CHECK-NEXT:   [[ptr:%\d+]] = OpAccessChain %_ptr_Input_float %gl_ClipDistance %uint_2
+// CHECK-NEXT: [[clip1:%\d+]] = OpLoad %float [[ptr]]
+// CHECK-NEXT:                  OpStore %param_var_clip1 [[clip1]]
+
+// CHECK-NEXT:  [[ptr0:%\d+]] = OpAccessChain %_ptr_Input_float %gl_CullDistance %uint_0
+// CHECK-NEXT:  [[val0:%\d+]] = OpLoad %float [[ptr0]]
+// CHECK-NEXT:  [[ptr1:%\d+]] = OpAccessChain %_ptr_Input_float %gl_CullDistance %uint_1
+// CHECK-NEXT:  [[val1:%\d+]] = OpLoad %float [[ptr1]]
+// CHECK-NEXT:  [[ptr2:%\d+]] = OpAccessChain %_ptr_Input_float %gl_CullDistance %uint_2
+// CHECK-NEXT:  [[val2:%\d+]] = OpLoad %float [[ptr2]]
+// CHECK-NEXT: [[cull1:%\d+]] = OpCompositeConstruct %v3float [[val0]] [[val1]] [[val2]]
+// CHECK-NEXT:                  OpStore %param_var_cull1 [[cull1]]
+
+// CHECK-NEXT:   [[bar:%\d+]] = OpLoad %float %in_var_BAR
+// CHECK-NEXT:                  OpStore %param_var_bar [[bar]]
+
+// CHECK-NEXT:   [[ret:%\d+]] = OpFunctionCall %v4float %src_main %param_var_psIn %param_var_clip1 %param_var_cull1 %param_var_bar
+// CHECK-NEXT:                  OpStore %out_var_SV_Target [[ret]]
+}

+ 126 - 0
tools/clang/test/CodeGenSPIRV/spirv.interface.vs.hlsl

@@ -0,0 +1,126 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK: OpEntryPoint Vertex %main "main" %gl_PerVertexOut %in_var_TEXCOORD %in_var_SV_Position %out_var_COLOR %out_var_TEXCOORD
+
+// CHECK: OpMemberDecorate %type_gl_PerVertex 0 BuiltIn Position
+// CHECK: OpMemberDecorate %type_gl_PerVertex 1 BuiltIn PointSize
+// CHECK: OpMemberDecorate %type_gl_PerVertex 2 BuiltIn ClipDistance
+// CHECK: OpMemberDecorate %type_gl_PerVertex 3 BuiltIn CullDistance
+// CHECK: OpDecorate %type_gl_PerVertex Block
+
+// CHECK: OpDecorate %in_var_TEXCOORD Location 0
+// CHECK: OpDecorate %in_var_SV_Position Location 1
+// CHECK: OpDecorate %out_var_COLOR Location 0
+// CHECK: OpDecorate %out_var_TEXCOORD Location 1
+
+//     clipdis0 + clipdis1            : 5 floats
+//     culldis3 + culldis5 + culldis6 : 3 floats
+// CHECK: %type_gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_5 %_arr_float_uint_3
+
+// CHECK: %gl_PerVertexOut = OpVariable %_ptr_Output_type_gl_PerVertex Output
+
+// CHECK: %in_var_TEXCOORD = OpVariable %_ptr_Input_v4float Input
+// CHECK: %in_var_SV_Position = OpVariable %_ptr_Input_v4float Input
+// CHECK: %out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+// CHECK: %out_var_TEXCOORD = OpVariable %_ptr_Output_v4float Output
+
+struct InnerInnerStruct {
+  float4           position : SV_Position;      // -> BuiltIn Position in gl_Pervertex
+};
+
+struct InnerStruct {
+  float2           clipdis1 : SV_ClipDistance1; // -> BuiltIn ClipDistance in gl_PerVertex
+  InnerInnerStruct s;
+};
+
+struct VSOut {
+  float4           color    : COLOR;            // -> Output variable
+  InnerStruct s;
+};
+
+void main(out VSOut  vsOut,
+          out   float3 clipdis0 : SV_ClipDistance0, // -> BuiltIn ClipDistance in gl_PerVertex
+          inout float4 coord    : TEXCOORD,         // -> Input & output variable
+          out   float  culldis5 : SV_CullDistance5, // -> BuiltIn CullDistance in gl_PerVertex
+          out   float  culldis3 : SV_CullDistance3, // -> BuiltIn CullDistance in gl_PerVertex
+          out   float  culldis6 : SV_CullDistance6, // -> BuiltIn CullDistance in gl_PerVertex
+          in    float4 inPos    : SV_Position       // -> Input variable
+         ) {
+    vsOut    = (VSOut)0;
+    clipdis0 = 1.;
+    coord    = 2.;
+    culldis5 = 3.;
+    culldis3 = 4.;
+    culldis6 = 5.;
+    inPos    = 6.;
+
+// Layout of ClipDistance array:
+//   clipdis0: 3 floats, offset 0
+//   clipdis1: 2 floats, offset 3
+
+// Layout of CullDistance array:
+//   culldis3: 1 floats, offset 0
+//   culldis5: 1 floats, offset 1
+//   culldis6: 1 floats, offset 2
+
+// CHECK:      [[texcoord:%\d+]] = OpLoad %v4float %in_var_TEXCOORD
+// CHECK-NEXT:                     OpStore %param_var_coord [[texcoord]]
+// CHECK-NEXT:      [[pos:%\d+]] = OpLoad %v4float %in_var_SV_Position
+// CHECK-NEXT:                     OpStore %param_var_inPos [[pos]]
+
+// CHECK-NEXT: OpFunctionCall %void %src_main
+
+// Write out COLOR
+// CHECK-NEXT:    [[vsOut:%\d+]] = OpLoad %VSOut %param_var_vsOut
+// CHECK-NEXT: [[outColor:%\d+]] = OpCompositeExtract %v4float [[vsOut]] 0
+// CHECK-NEXT:                     OpStore %out_var_COLOR [[outColor]]
+
+// CHECK-NEXT:   [[innerS:%\d+]] = OpCompositeExtract %InnerStruct [[vsOut]] 1
+
+// Write out SV_ClipDistance1
+// CHECK-NEXT:    [[clip1:%\d+]] = OpCompositeExtract %v2float [[innerS]] 0
+// CHECK-NEXT: [[clipArr3:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_3
+// CHECK-NEXT:   [[clip10:%\d+]] = OpCompositeExtract %float [[clip1]] 0
+// CHECK-NEXT:                     OpStore [[clipArr3]] [[clip10]]
+// CHECK-NEXT: [[clipArr4:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_4
+// CHECK-NEXT:   [[clip11:%\d+]] = OpCompositeExtract %float [[clip1]] 1
+// CHECK-NEXT:                     OpStore [[clipArr4]] [[clip11]]
+
+// CHECK-NEXT: [[inner2S:%\d+]] = OpCompositeExtract %InnerInnerStruct [[innerS]] 1
+
+// Write out SV_Position
+// CHECK-NEXT:     [[pos:%\d+]] = OpCompositeExtract %v4float [[inner2S]] 0
+// CHECK-NEXT:  [[outPos:%\d+]] = OpAccessChain %_ptr_Output_v4float %gl_PerVertexOut %uint_0
+// CHECK-NEXT:       OpStore [[outPos]] [[pos]]
+
+// Write out SV_ClipDistance0
+// CHECK-NEXT:    [[clip0:%\d+]] = OpLoad %v3float %param_var_clipdis0
+// CHECK-NEXT: [[clipArr0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_0
+// CHECK-NEXT:   [[clip00:%\d+]] = OpCompositeExtract %float [[clip0]] 0
+// CHECK-NEXT:                     OpStore [[clipArr0]] [[clip00]]
+// CHECK-NEXT: [[clipArr1:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_1
+// CHECK-NEXT:   [[clip01:%\d+]] = OpCompositeExtract %float [[clip0]] 1
+// CHECK-NEXT:                     OpStore [[clipArr1]] [[clip01]]
+// CHECK-NEXT: [[clipArr2:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_2 %uint_2
+// CHECK-NEXT:   [[clip02:%\d+]] = OpCompositeExtract %float [[clip0]] 2
+// CHECK-NEXT:                     OpStore [[clipArr2]] [[clip02]]
+
+// Write out TEXCOORD
+// CHECK-NEXT:  [[texcord:%\d+]] = OpLoad %v4float %param_var_coord
+// CHECK-NEXT:                     OpStore %out_var_TEXCOORD [[texcord]]
+
+// Write out SV_CullDistance5
+// CHECK-NEXT:    [[cull5:%\d+]] = OpLoad %float %param_var_culldis5
+// CHECK-NEXT: [[cullArr1:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_3 %uint_1
+// CHECK-NEXT:                     OpStore [[cullArr1]] [[cull5]]
+
+// Write out SV_CullDistance3
+// CHECK-NEXT:    [[cull3:%\d+]] = OpLoad %float %param_var_culldis3
+// CHECK-NEXT: [[cullArr0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_3 %uint_0
+// CHECK-NEXT:                     OpStore [[cullArr0]] [[cull3]]
+
+// Write out SV_CullDistance6
+// CHECK-NEXT:    [[cull6:%\d+]] = OpLoad %float %param_var_culldis6
+// CHECK-NEXT: [[cullArr2:%\d+]] = OpAccessChain %_ptr_Output_float %gl_PerVertexOut %uint_3 %uint_2
+// CHECK-NEXT:                     OpStore [[cullArr2]] [[cull6]]
+}

+ 4 - 3
tools/clang/test/CodeGenSPIRV/type.line-stream.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T gs_6_0 -E main
 
-// CHECK: OpEntryPoint Geometry %main "main" {{%\w+}} %gl_Position %out_var_COLOR0 %out_var_TEXCOORD0
+// CHECK: OpEntryPoint Geometry %main "main"
+// CHECK-SAME: %gl_Position %out_var_COLOR0 %out_var_TEXCOORD0
 
 // CHECK: OpExecutionMode %main OutputLineStrip
 
@@ -21,13 +22,13 @@
 
 
 struct GS_OUT
-{ 
+{
   float4 position : SV_POSITION;
   float4 color    : COLOR0;
   float2 uv       : TEXCOORD0;
 };
 
-[maxvertexcount(3)] 
+[maxvertexcount(3)]
 void main(triangle float4 id[3] : VertexID, inout LineStream <GS_OUT> outstream)
 {
 }

+ 1 - 1
tools/clang/test/CodeGenSPIRV/type.matrix.hlsl

@@ -1,4 +1,4 @@
-// Run: %dxc -T vs_6_0 -E main
+// Run: %dxc -T ps_6_0 -E main
 
 // NOTE: According to Item "Data rules" of SPIR-V Spec 2.16.1 "Universal
 // Validation Rules":

+ 3 - 2
tools/clang/test/CodeGenSPIRV/type.point-stream.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T gs_6_0 -E main
 
-// CHECK: OpEntryPoint Geometry %main "main" {{%\w+}} %gl_Position %out_var_COLOR0 %out_var_TEXCOORD0
+// CHECK: OpEntryPoint Geometry %main "main"
+// CHECK-SAME: %gl_Position %out_var_COLOR0 %out_var_TEXCOORD0
 
 // CHECK: OpExecutionMode %main OutputPoints
 
@@ -26,7 +27,7 @@ struct GS_OUT
   float2 uv       : TEXCOORD0;
 };
 
-[maxvertexcount(3)] 
+[maxvertexcount(3)]
 void main(triangle float4 id[3] : VertexID, inout PointStream <GS_OUT> outstream)
 {
 }

+ 4 - 3
tools/clang/test/CodeGenSPIRV/type.triangle-stream.hlsl

@@ -1,6 +1,7 @@
 // Run: %dxc -T gs_6_0 -E main
 
-// CHECK: OpEntryPoint Geometry %main "main" {{%\w+}} %gl_Position %out_var_COLOR0 %out_var_TEXCOORD0
+// CHECK: OpEntryPoint Geometry %main "main"
+// CHECK-SAME: %gl_Position %out_var_COLOR0 %out_var_TEXCOORD0
 
 // CHECK: OpExecutionMode %main OutputTriangleStrip
 
@@ -20,13 +21,13 @@
 // CHECK: %outstream = OpFunctionParameter %_ptr_Function_GS_OUT
 
 struct GS_OUT
-{ 
+{
   float4 position : SV_POSITION;
   float4 color    : COLOR0;
   float2 uv       : TEXCOORD0;
 };
 
-[maxvertexcount(3)] 
+[maxvertexcount(3)]
 void main(triangle float4 id[3] : VertexID, inout TriangleStream <GS_OUT> outstream)
 {
 }

+ 21 - 17
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -348,18 +348,8 @@ TEST_F(FileTest, ClassStaticMember) {
 }
 
 // For semantics
-TEST_F(FileTest, SemanticPositionVS) {
-  runFileTest("semantic.position.vs.hlsl");
-}
-TEST_F(FileTest, SemanticPositionPS) {
-  runFileTest("semantic.position.ps.hlsl");
-}
-TEST_F(FileTest, SemanticPositionDS) {
-  runFileTest("semantic.position.ds.hlsl");
-}
-TEST_F(FileTest, SemanticPositionGS) {
-  runFileTest("semantic.position.gs.hlsl");
-}
+// SV_Position, SV_ClipDistance, and SV_CullDistance are covered in
+// SpirvStageIOInterface* tests.
 TEST_F(FileTest, SemanticVertexIDVS) {
   runFileTest("semantic.vertex-id.vs.hlsl");
 }
@@ -720,7 +710,7 @@ TEST_F(FileTest, PrimitiveErrorGS) {
   runFileTest("primitive.error.gs.hlsl", /*expectSuccess*/ false);
 }
 
-// Vulkan/SPIR-V specific
+// SPIR-V specific
 TEST_F(FileTest, SpirvStorageClass) { runFileTest("spirv.storage-class.hlsl"); }
 
 TEST_F(FileTest, SpirvEntryFunctionWrapper) {
@@ -730,6 +720,21 @@ TEST_F(FileTest, SpirvEntryFunctionInOut) {
   runFileTest("spirv.entry-function.inout.hlsl");
 }
 
+// For shader stage input/output interface
+// For semantic SV_Position, SV_ClipDistance, SV_CullDistance
+TEST_F(FileTest, SpirvStageIOInterfaceVS) {
+  runFileTest("spirv.interface.vs.hlsl");
+}
+TEST_F(FileTest, SpirvStageIOInterfaceHS) {
+  runFileTest("spirv.interface.hs.hlsl");
+}
+TEST_F(FileTest, SpirvStageIOInterfaceDS) {
+  runFileTest("spirv.interface.ds.hlsl");
+}
+TEST_F(FileTest, SpirvStageIOInterfacePS) {
+  runFileTest("spirv.interface.ps.hlsl");
+}
+
 TEST_F(FileTest, SpirvInterpolation) {
   runFileTest("spirv.interpolation.hlsl");
 }
@@ -741,6 +746,7 @@ TEST_F(FileTest, VulkanAttributeErrors) {
   runFileTest("vk.attribute.error.hlsl", /*expectSuccess*/ false);
 }
 
+// Vulkan specific
 TEST_F(FileTest, VulkanLocation) { runFileTest("vk.location.hlsl"); }
 TEST_F(FileTest, VulkanLocationInputExplicitOutputImplicit) {
   runFileTest("vk.location.exp-in.hlsl");
@@ -818,13 +824,11 @@ TEST_F(FileTest, HullShaderPCFTakesOutputPatch) {
   runFileTest("hull.pcf.output-patch.hlsl");
 }
 TEST_F(FileTest, HullShaderPCFTakesPrimitiveId) {
-  runFileTest("hull.pcf.primitive-id.hlsl");
+  runFileTest("hull.pcf.primitive-id.1.hlsl");
 }
 TEST_F(FileTest, HullShaderPCFTakesPrimitiveIdButMainDoesnt) {
-  runFileTest("hull.pcf.primitive-id-2.hlsl");
+  runFileTest("hull.pcf.primitive-id.2.hlsl");
 }
-// For Hull Shader Output variables
-TEST_F(FileTest, HullShaderOutputVars) { runFileTest("hull.output-vars.hlsl"); }
 // For the structure of Hull Shaders
 TEST_F(FileTest, HullShaderStructure) { runFileTest("hull.structure.hlsl"); }