2
0
Эх сурвалжийг харах

[spirv] Support several SV semantics in VS and PS (#534)

Includes: SV_Position, SV_Target, SV_VertexID, SV_InstanceID,
and SV_Depth.

Also use existing DXIL classes for stage variables. There already
exist classes for semantics and SigPoints. Reuse them to create a
StageVar class for holding information about stage variables.
Lei Zhang 8 жил өмнө
parent
commit
d153fe8e96

+ 139 - 71
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -9,23 +9,22 @@
 
 #include "DeclResultIdMapper.h"
 
+#include "dxc/HLSL/DxilConstants.h"
+#include "dxc/HLSL/DxilTypeSystem.h"
 #include "clang/AST/HlslTypes.h"
-#include "llvm/ADT/StringSwitch.h"
 
 namespace clang {
 namespace spirv {
 
-void DeclResultIdMapper::createStageVarFromFnReturn(
+bool DeclResultIdMapper::createStageVarFromFnReturn(
     const FunctionDecl *funcDecl) {
   // SemanticDecl for the return value is attached to the FunctionDecl.
-  createStageVariables(funcDecl, false);
+  return createStageVariables(funcDecl, true);
 }
 
-void DeclResultIdMapper::createStageVarFromFnParam(
+bool DeclResultIdMapper::createStageVarFromFnParam(
     const ParmVarDecl *paramDecl) {
-  // TODO: We cannot treat all parameters as stage inputs because of
-  // out/input modifiers.
-  createStageVariables(paramDecl, true);
+  return createStageVariables(paramDecl, false);
 }
 
 void DeclResultIdMapper::registerDeclResultId(const NamedDecl *symbol,
@@ -34,7 +33,10 @@ void DeclResultIdMapper::registerDeclResultId(const NamedDecl *symbol,
 }
 
 bool DeclResultIdMapper::isStageVariable(uint32_t varId) const {
-  return stageVars.count(varId) != 0;
+  for (const auto &var : stageVars)
+    if (var.getSpirvId() == varId)
+      return true;
+  return false;
 }
 
 uint32_t DeclResultIdMapper::getDeclResultId(const NamedDecl *decl) const {
@@ -76,19 +78,12 @@ DeclResultIdMapper::getNormalDeclResultId(const NamedDecl *decl) const {
 }
 
 std::vector<uint32_t> DeclResultIdMapper::collectStageVariables() const {
-  std::vector<uint32_t> stageVars;
+  std::vector<uint32_t> vars;
 
-  for (const auto &builtin : stageBuiltins) {
-    stageVars.push_back(builtin.first);
-  }
-  for (const auto &input : stageInputs) {
-    stageVars.push_back(input.first);
-  }
-  for (const auto &output : stageOutputs) {
-    stageVars.push_back(output.first);
-  }
+  for (const auto &var : stageVars)
+    vars.push_back(var.getSpirvId());
 
-  return stageVars;
+  return vars;
 }
 
 void DeclResultIdMapper::finalizeStageIOLocations() {
@@ -97,11 +92,14 @@ void DeclResultIdMapper::finalizeStageIOLocations() {
 
   // TODO: sort the variables according to some criteria first, e.g.,
   // alphabetical order of semantic names.
-  for (const auto &input : stageInputs) {
-    theBuilder.decorateLocation(input.first, nextInputLocation++);
-  }
-  for (const auto &output : stageOutputs) {
-    theBuilder.decorateLocation(output.first, nextOutputLocation++);
+  for (auto &var : stageVars) {
+    if (!var.isSpirvBuitin()) {
+      if (var.getSigPoint()->IsInput()) {
+        theBuilder.decorateLocation(var.getSpirvId(), nextInputLocation++);
+      } else if (var.getSigPoint()->IsOutput()) {
+        theBuilder.decorateLocation(var.getSpirvId(), nextOutputLocation++);
+      }
+    }
   }
 }
 
@@ -113,49 +111,49 @@ DeclResultIdMapper::getFnParamOrRetType(const DeclaratorDecl *decl) const {
   return decl->getType();
 }
 
-void DeclResultIdMapper::createStageVariables(const DeclaratorDecl *decl,
-                                              bool actAsInput) {
+bool DeclResultIdMapper::createStageVariables(const DeclaratorDecl *decl,
+                                              bool forRet) {
   QualType type = getFnParamOrRetType(decl);
 
   if (type->isVoidType()) {
     // No stage variables will be created for void type.
-    return;
+    return true;
   }
 
-  const std::string semantic = getStageVarSemantic(decl);
-  if (!semantic.empty()) {
+  const llvm::StringRef semanticStr = getStageVarSemantic(decl);
+  if (!semanticStr.empty()) {
     // Found semantic attached directly to this Decl. This means we need to
     // map this decl to a single stage variable.
     const uint32_t typeId = typeTranslator.translateType(type);
-    const auto kind = getStageVarKind(semantic);
-
-    if (actAsInput) {
-      // Stage (builtin) input variable cases
-      const uint32_t varId =
-          theBuilder.addStageIOVariable(typeId, spv::StorageClass::Input);
-
-      stageInputs.push_back(std::make_pair(varId, semantic));
-      remappedDecls[decl] = varId;
-      stageVars.insert(varId);
-    } else {
-      // Handle output builtin variables first
-      if (shaderModel.IsVS() && kind == StageVarKind::Position) {
-        const uint32_t varId =
-            theBuilder.addStageBuiltinVariable(typeId, spv::BuiltIn::Position);
-
-        stageBuiltins.push_back(std::make_pair(varId, semantic));
-        remappedDecls[decl] = varId;
-        stageVars.insert(varId);
-      } else {
-        // The rest are normal stage output variables
-        const uint32_t varId =
-            theBuilder.addStageIOVariable(typeId, spv::StorageClass::Output);
-
-        stageOutputs.push_back(std::make_pair(varId, semantic));
-        remappedDecls[decl] = varId;
-        stageVars.insert(varId);
-      }
+
+    // TODO: fix this when supporting parameter in/out qualifiers
+    const hlsl::DxilParamInputQual qual =
+        forRet ? hlsl::DxilParamInputQual::Out : hlsl::DxilParamInputQual::In;
+
+    // TODO: use the correct isPC value when supporting patch constant function
+    // in hull shader
+    const hlsl::SigPoint *sigPoint =
+        hlsl::SigPoint::GetSigPoint(hlsl::SigPointFromInputQual(
+            qual, shaderModel.GetKind(), /*isPC*/ false));
+
+    const auto *semantic = hlsl::Semantic::GetByName(semanticStr);
+
+    // 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")
+          << semanticStr << shaderModel.GetName();
+      return false;
     }
+
+    StageVar stageVar(sigPoint, semantic, typeId);
+    const uint32_t varId = createSpirvStageVar(&stageVar);
+    stageVar.setSpirvId(varId);
+
+    stageVars.push_back(stageVar);
+    remappedDecls[decl] = varId;
   } else {
     // If the decl itself doesn't have semantic, it should be a struct having
     // all its fields with semantics.
@@ -166,30 +164,100 @@ void DeclResultIdMapper::createStageVariables(const DeclaratorDecl *decl,
 
     // Recursively handle all the fields.
     for (const auto *field : structDecl->fields()) {
-      createStageVariables(field, actAsInput);
+      if (!createStageVariables(field, forRet))
+        return false;
     }
   }
+
+  return true;
 }
 
-DeclResultIdMapper::StageVarKind
-DeclResultIdMapper::getStageVarKind(llvm::StringRef semantic) const {
-  return llvm::StringSwitch<StageVarKind>(semantic)
-      .Case("", StageVarKind::None)
-      .StartsWith("COLOR", StageVarKind::Color)
-      .StartsWith("POSITION", StageVarKind::Position)
-      .StartsWith("SV_POSITION", StageVarKind::Position)
-      .StartsWith("SV_TARGET", StageVarKind::Target)
-      .Default(StageVarKind::Arbitary);
+uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) {
+  const auto semanticKind = stageVar->getSemantic()->GetKind();
+  const auto sigPointKind = stageVar->getSigPoint()->GetKind();
+  const uint32_t type = stageVar->getSpirvTypeId();
+
+  // The following translation assumes that semantic validity in the current
+  // shader model is already checked, so it only covers valid SigPoints for
+  // each semantic.
+
+  switch (semanticKind) {
+  // 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 decoration can only be used
+  // by PSOut, HS/DS/GS In/Out.
+  case hlsl::Semantic::Kind::Position: {
+    switch (sigPointKind) {
+    case hlsl::SigPoint::Kind::VSIn:
+      return theBuilder.addStageIOVariable(type, spv::StorageClass::Input);
+    case hlsl::SigPoint::Kind::VSOut:
+      stageVar->setIsSpirvBuiltin();
+      return theBuilder.addStageBuiltinVariable(type, spv::BuiltIn::Position);
+    case hlsl::SigPoint::Kind::PSIn:
+      stageVar->setIsSpirvBuiltin();
+      return theBuilder.addStageBuiltinVariable(type, spv::BuiltIn::FragCoord);
+    default:
+      emitError("semantic Position for SigPoint %0 unimplemented yet")
+          << stageVar->getSigPoint()->GetName();
+      break;
+    }
+  }
+  // According to DXIL spec, the VertexID SV can only be used by VSIn.
+  case hlsl::Semantic::Kind::VertexID:
+    stageVar->setIsSpirvBuiltin();
+    return theBuilder.addStageBuiltinVariable(type, spv::BuiltIn::VertexIndex);
+  // According to DXIL spec, the InstanceID SV can  be used by VSIn, VSOut,
+  // HSCPIn, HSCPOut, DSCPIn, DSOut, GSVIn, GSOut, PSIn.
+  // According to Vulkan spec, the InstanceIndex can only be used by VSIn.
+  case hlsl::Semantic::Kind::InstanceID: {
+    switch (sigPointKind) {
+    case hlsl::SigPoint::Kind::VSIn:
+      stageVar->setIsSpirvBuiltin();
+      return theBuilder.addStageBuiltinVariable(type,
+                                                spv::BuiltIn::InstanceIndex);
+    case hlsl::SigPoint::Kind::VSOut:
+      return theBuilder.addStageIOVariable(type, spv::StorageClass::Output);
+    case hlsl::SigPoint::Kind::PSIn:
+      return theBuilder.addStageIOVariable(type, spv::StorageClass::Input);
+    default:
+      emitError("semantic InstanceID for SigPoint %0 unimplemented yet")
+          << stageVar->getSigPoint()->GetName();
+      break;
+    }
+  }
+  // According to DXIL spec, the Depth SV can only be used by PSOut.
+  case hlsl::Semantic::Kind::Depth:
+    stageVar->setIsSpirvBuiltin();
+    return theBuilder.addStageBuiltinVariable(type, spv::BuiltIn::FragDepth);
+  // According to DXIL spec, the Target SV can only be used by PSOut.
+  // There is no corresponding builtin decoration in SPIR-V. So generate normal
+  // Vulkan stage input/output variables.
+  case hlsl::Semantic::Kind::Target:
+  // An arbitrary semantic is defined by users. Generate normal Vulkan stage
+  // input/output variables.
+  case hlsl::Semantic::Kind::Arbitrary: {
+    if (stageVar->getSigPoint()->IsInput())
+      return theBuilder.addStageIOVariable(type, spv::StorageClass::Input);
+    if (stageVar->getSigPoint()->IsOutput())
+      return theBuilder.addStageIOVariable(type, spv::StorageClass::Output);
+    // TODO: patch constant function in hull shader
+  }
+  default:
+    emitError("semantic %0 unimplemented yet")
+        << stageVar->getSemantic()->GetName();
+    break;
+  }
+
+  return 0;
 }
 
-std::string
-DeclResultIdMapper::getStageVarSemantic(const NamedDecl *decl) const {
+llvm::StringRef DeclResultIdMapper::getStageVarSemantic(const NamedDecl *decl) {
   for (auto *annotation : decl->getUnusualAnnotations()) {
     if (auto *semantic = dyn_cast<hlsl::SemanticDecl>(annotation)) {
-      return semantic->SemanticName.upper();
+      return semantic->SemanticName;
     }
   }
-  return "";
+  return {};
 }
 
 } // end namespace spirv

+ 66 - 40
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -10,14 +10,15 @@
 #ifndef LLVM_CLANG_LIB_SPIRV_DECLRESULTIDMAPPER_H
 #define LLVM_CLANG_LIB_SPIRV_DECLRESULTIDMAPPER_H
 
-#include <string>
 #include <vector>
 
-#include "dxc/HLSL//DxilShaderModel.h"
+#include "dxc/HLSL/DxilSemantic.h"
+#include "dxc/HLSL/DxilShaderModel.h"
+#include "dxc/HLSL/DxilSigPoint.h"
 #include "spirv/1.0/spirv.hpp11"
 #include "clang/SPIRV/ModuleBuilder.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/SmallVector.h"
 
 #include "TypeTranslator.h"
@@ -25,6 +26,44 @@
 namespace clang {
 namespace spirv {
 
+/// \brief The class containing HLSL and SPIR-V information about a Vulkan stage
+/// (builtin/input/output) variable.
+class StageVar {
+public:
+  StageVar(const hlsl::SigPoint *sig, const hlsl::Semantic *sema, uint32_t type)
+      : sigPoint(sig), semantic(sema), typeId(type), valueId(0),
+        isBuiltin(false), location(llvm::None) {}
+
+  const hlsl::SigPoint *getSigPoint() const { return sigPoint; }
+  const hlsl::Semantic *getSemantic() const { return semantic; }
+
+  uint32_t getSpirvTypeId() const { return typeId; }
+
+  uint32_t getSpirvId() const { return valueId; }
+  void setSpirvId(uint32_t id) { valueId = id; }
+
+  bool isSpirvBuitin() const { return isBuiltin; }
+  void setIsSpirvBuiltin() { isBuiltin = true; }
+
+  bool hasLocation() const { return location.hasValue(); }
+  void setLocation(uint32_t loc) { location = llvm::Optional<uint32_t>(loc); }
+
+private:
+  /// HLSL SigPoint. It uniquely identifies each set of parameters that may be
+  /// input or output for each entry point.
+  const hlsl::SigPoint *sigPoint;
+  /// HLSL semantic.
+  const hlsl::Semantic *semantic;
+  /// SPIR-V <type-id>.
+  uint32_t typeId;
+  /// SPIR-V <result-id>.
+  uint32_t valueId;
+  /// Indicates whether this stage variable should be a SPIR-V builtin.
+  bool isBuiltin;
+  /// Location assignment if input/output variable.
+  llvm::Optional<uint32_t> location;
+};
+
 /// \brief The class containing mappings from Clang frontend Decls to their
 /// corresponding SPIR-V <result-id>s.
 ///
@@ -54,12 +93,12 @@ public:
                             ModuleBuilder &builder, DiagnosticsEngine &diag);
 
   /// \brief Creates the stage variables by parsing the semantics attached to
-  /// the given function's return value.
-  void createStageVarFromFnReturn(const FunctionDecl *funcDecl);
+  /// the given function's return value and returns true on success.
+  bool createStageVarFromFnReturn(const FunctionDecl *funcDecl);
 
   /// \brief Creates the stage variables by parsing the semantics attached to
-  /// the given function's parameter.
-  void createStageVarFromFnParam(const ParmVarDecl *paramDecl);
+  /// the given function's parameter and returns true on success.
+  bool createStageVarFromFnParam(const ParmVarDecl *paramDecl);
 
   /// \brief Registers a decl's <result-id> without generating any SPIR-V
   /// instruction. The given decl will be treated as normal decl.
@@ -98,64 +137,51 @@ public:
   void finalizeStageIOLocations();
 
 private:
-  /// \brief Stage variable kind.
-  ///
-  /// Stage variables include builtin, input, and output variables.
-  /// They participate in interface matching in Vulkan pipelines.
-  enum class StageVarKind {
-    None,
-    Arbitary,
-    Position,
-    Color,
-    Target,
-    // TODO: other possible kinds
-  };
-
-  using StageVarIdSemanticPair = std::pair<uint32_t, std::string>;
+  /// \brief Wrapper method to create an error message and report it
+  /// in the diagnostic engine associated with this consumer.
+  template <unsigned N> DiagnosticBuilder emitError(const char (&message)[N]) {
+    const auto diagId =
+        diags.getCustomDiagID(clang::DiagnosticsEngine::Error, message);
+    return diags.Report(diagId);
+  }
 
   /// 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 all the stage variables mapped from semantics on the given decl.
+  /// Creates all the stage variables mapped from semantics on the given decl
+  /// and returns true on success.
   ///
   /// Assumes the decl has semantic attached to itself or to its fields.
-  void createStageVariables(const DeclaratorDecl *decl, bool actAsInput);
+  bool createStageVariables(const DeclaratorDecl *decl, bool forReturnValue);
 
-  /// \brief Returns the stage variable's kind for the given semantic.
-  StageVarKind getStageVarKind(llvm::StringRef semantic) const;
+  /// 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
+  /// accordingly.
+  uint32_t createSpirvStageVar(StageVar *);
 
   /// \brief Returns the stage variable's semantic for the given Decl.
-  std::string getStageVarSemantic(const NamedDecl *decl) const;
+  static llvm::StringRef getStageVarSemantic(const NamedDecl *decl);
 
 private:
   const hlsl::ShaderModel &shaderModel;
   ModuleBuilder &theBuilder;
   TypeTranslator typeTranslator;
+  DiagnosticsEngine &diags;
 
   /// Mapping of all remapped decls to their <result-id>s.
   llvm::DenseMap<const NamedDecl *, uint32_t> remappedDecls;
   /// Mapping of all normal decls to their <result-id>s.
   llvm::DenseMap<const NamedDecl *, uint32_t> normalDecls;
-  /// <result-id>s of all defined stage variables.
-  ///
-  /// We need to keep a separate list here to avoid looping through the
-  /// remappedDecls to find whether an <result-id> is for a stage variable.
-  llvm::SmallSet<uint32_t, 16> stageVars;
-
-  /// Stage input/oupt/builtin variables and their kinds.
-  ///
-  /// We need to keep a separate list here in order to sort them at the end
-  /// of the module building.
-  llvm::SmallVector<StageVarIdSemanticPair, 8> stageInputs;
-  llvm::SmallVector<StageVarIdSemanticPair, 8> stageOutputs;
-  llvm::SmallVector<StageVarIdSemanticPair, 8> stageBuiltins;
+  /// Vector of all defined stage variables.
+  llvm::SmallVector<StageVar, 8> stageVars;
 };
 
 DeclResultIdMapper::DeclResultIdMapper(const hlsl::ShaderModel &model,
                                        ModuleBuilder &builder,
                                        DiagnosticsEngine &diag)
-    : shaderModel(model), theBuilder(builder), typeTranslator(builder, diag) {}
+    : shaderModel(model), theBuilder(builder), typeTranslator(builder, diag),
+      diags(diag) {}
 
 } // end namespace spirv
 } // end namespace clang

+ 1 - 0
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -300,6 +300,7 @@ uint32_t ModuleBuilder::addStageBuiltinVariable(uint32_t type,
   switch (builtin) {
   case spv::BuiltIn::Position:
   case spv::BuiltIn::PointSize:
+  case spv::BuiltIn::FragDepth:
     // TODO: add the rest output builtins
     sc = spv::StorageClass::Output;
     break;

+ 5 - 2
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -364,9 +364,12 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
 
   if (funcName == entryFunctionName) {
     // First create stage variables for the entry point.
-    declIdMapper.createStageVarFromFnReturn(decl);
+    if (!declIdMapper.createStageVarFromFnReturn(decl))
+      return;
+
     for (const auto *param : decl->params())
-      declIdMapper.createStageVarFromFnParam(param);
+      if (!declIdMapper.createStageVarFromFnParam(param))
+        return;
 
     // Construct the function signature.
     const uint32_t voidType = theBuilder.getVoidType();

+ 3 - 2
tools/clang/test/CodeGenSPIRV/passthru-ps.hlsl2spv

@@ -1,4 +1,5 @@
 // Run: %dxc -T ps_6_0 -E main
+
 float4 main(float4 input: COLOR): SV_TARGET
 {
     return input;
@@ -12,12 +13,12 @@ float4 main(float4 input: COLOR): SV_TARGET
 // ; Schema: 0
 // OpCapability Shader
 // OpMemoryModel Logical GLSL450
-// OpEntryPoint Fragment %main "main" %6 %4
+// OpEntryPoint Fragment %main "main" %4 %6
 // OpExecutionMode %main OriginUpperLeft
 // OpName %main "main"
 // OpName %bb_entry "bb.entry"
-// OpDecorate %6 Location 0
 // OpDecorate %4 Location 0
+// OpDecorate %6 Location 0
 // %float = OpTypeFloat 32
 // %v4float = OpTypeVector %float 4
 // %_ptr_Output_v4float = OpTypePointer Output %v4float

+ 3 - 3
tools/clang/test/CodeGenSPIRV/passthru-vs.hlsl2spv

@@ -1,7 +1,7 @@
 // Run: %dxc -T vs_6_0 -E VSmain
 
 struct PSInput {
-  float4 position : POSITION;
+  float4 position : SV_POSITION;
   float4 color : COLOR;
 };
 
@@ -21,14 +21,14 @@ PSInput VSmain(float4 position: POSITION, float4 color: COLOR) {
 // ; Schema: 0
 // OpCapability Shader
 // OpMemoryModel Logical GLSL450
-// OpEntryPoint Vertex %VSmain "VSmain" %gl_Position %7 %8 %5
+// OpEntryPoint Vertex %VSmain "VSmain" %gl_Position %5 %7 %8
 // OpName %VSmain "VSmain"
 // OpName %bb_entry "bb.entry"
 // OpName %result "result"
 // OpDecorate %gl_Position BuiltIn Position
+// OpDecorate %5 Location 0
 // OpDecorate %7 Location 0
 // OpDecorate %8 Location 1
-// OpDecorate %5 Location 0
 // %int = OpTypeInt 32 1
 // %float = OpTypeFloat 32
 // %v4float = OpTypeVector %float 4

+ 17 - 0
tools/clang/test/CodeGenSPIRV/semantic.arbitrary.hlsl

@@ -0,0 +1,17 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK: OpEntryPoint Vertex %main "main" [[out:%\d+]] [[a:%\d+]] [[b:%\d+]] [[c:%\d+]]
+
+// CHECK: OpDecorate [[out]] Location 0
+// CHECK: OpDecorate [[a]] Location 0
+// CHECK: OpDecorate [[b]] Location 1
+// CHECK: OpDecorate [[c]] Location 2
+
+// CHECK: [[out]] = OpVariable %_ptr_Output_float Output
+// CHECK: [[a]] = OpVariable %_ptr_Input_v4float Input
+// CHECK: [[b]] = OpVariable %_ptr_Input_int Input
+// CHECK: [[c]] = OpVariable %_ptr_Input_mat2v3float Input
+
+float main(float4 a: AAA, int b: B, float2x3 c: CC) : DDDD {
+    return 1.0;
+}

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

@@ -0,0 +1,11 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK:                 OpEntryPoint Fragment %main "main" %gl_FragDepth {{%\d+}}
+
+// CHECK:                 OpDecorate %gl_FragDepth BuiltIn FragDepth
+
+// CHECK: %gl_FragDepth = OpVariable %_ptr_Output_float Output
+
+float main(float input: A) : SV_Depth {
+    return input;
+}

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

@@ -0,0 +1,11 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK:             OpEntryPoint Fragment %main "main" {{%\d+}} [[input:%\d+]]
+
+// CHECK:             OpDecorate [[input]] Location 0
+
+// CHECK: [[input]] = OpVariable %_ptr_Input_int Input
+
+float4 main(int input: SV_InstanceID) : SV_Target {
+    return input;
+}

+ 14 - 0
tools/clang/test/CodeGenSPIRV/semantic.instance-id.vs.hlsl

@@ -0,0 +1,14 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK:                     OpEntryPoint Vertex %main "main" [[output:%\d+]] %gl_InstanceIndex
+
+// CHECK:                     OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex
+// CHECK:                     OpDecorate [[output]] Location 0
+
+// CHECK:        [[output]] = OpVariable %_ptr_Output_int Output
+// CHECK: %gl_InstanceIndex = OpVariable %_ptr_Input_int Input
+
+int main(int input: SV_InstanceID) : SV_InstanceID {
+    return input;
+}
+

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

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

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

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

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

@@ -0,0 +1,11 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK:              OpEntryPoint Fragment %main "main" [[target:%\d+]] {{%\d+}}
+
+// CHECK:              OpDecorate [[target]] Location 0
+
+// CHECK: [[target]] = OpVariable %_ptr_Output_v4float Output
+
+float4 main(float4 input: A) : SV_Target {
+    return input;
+}

+ 11 - 0
tools/clang/test/CodeGenSPIRV/semantic.vertex-id.vs.hlsl

@@ -0,0 +1,11 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK:                   OpEntryPoint Vertex %main "main" {{%\d+}} %gl_VertexIndex
+
+// CHECK:                   OpDecorate %gl_VertexIndex BuiltIn VertexIndex
+
+// CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_int Input
+
+int main(int input: SV_VertexID) : A {
+    return input;
+}

+ 20 - 0
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -217,6 +217,26 @@ TEST_F(FileTest, ControlFlowConditionalOp) { runFileTest("cf.cond-op.hlsl"); }
 // For function calls
 TEST_F(FileTest, FunctionCall) { runFileTest("fn.call.hlsl"); }
 
+// For semantics
+TEST_F(FileTest, SemanticPositionVS) {
+  runFileTest("semantic.position.vs.hlsl");
+}
+TEST_F(FileTest, SemanticPositionPS) {
+  runFileTest("semantic.position.ps.hlsl");
+}
+TEST_F(FileTest, SemanticVertexIDVS) {
+  runFileTest("semantic.vertex-id.vs.hlsl");
+}
+TEST_F(FileTest, SemanticInstanceIDVS) {
+  runFileTest("semantic.instance-id.vs.hlsl");
+}
+TEST_F(FileTest, SemanticInstanceIDPS) {
+  runFileTest("semantic.instance-id.ps.hlsl");
+}
+TEST_F(FileTest, SemanticTargetPS) { runFileTest("semantic.target.ps.hlsl"); }
+TEST_F(FileTest, SemanticDepthPS) { runFileTest("semantic.depth.ps.hlsl"); }
+TEST_F(FileTest, SemanticArbitrary) { runFileTest("semantic.arbitrary.hlsl"); }
+
 // For intrinsic functions
 TEST_F(FileTest, IntrinsicsDot) { runFileTest("intrinsics.dot.hlsl"); }
 TEST_F(FileTest, IntrinsicsAll) { runFileTest("intrinsics.all.hlsl"); }