Переглянути джерело

[spirv] Turn on C++11 attributes and support [[vk::location(X)]] (#568)

* [spirv] Turn on C++11 attributes and support [[vk::location(X)]]

This commit turns on C++11 attributes for HLSL globally. C++11
attributes will be parsed where they are allowed in C++11.
However, they will just result in an warning of ignored attribute,
without any semantic impact for the general DXIL CodeGen.

For SPIR-V CodeGen, C++11 attributes within the vk namespace will
be interpreted and converted to semantic AST nodes.

* [spirv] Respect [[vk::location(X)]] in SPIR-V CodeGen

SPIR-V CodeGen is modified to respect [[vk::location(X)]]
annotations, which takes precedence over implicit semantic
string ordering.

Added several tests for the annotation.
Lei Zhang 8 роки тому
батько
коміт
9fbe9bb349

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

@@ -232,6 +232,7 @@ def MicrosoftExt : LangOpt<"MicrosoftExt">;
 def Borland : LangOpt<"Borland">;
 def CUDA : LangOpt<"CUDA">;
 def COnly : LangOpt<"CPlusPlus", 1>;
+def SPIRV : LangOpt<"SPIRV">; // SPIRV Change
 
 // Defines targets for target-specific attributes. The list of strings should
 // specify architectures for which the target applies, based off the ArchType
@@ -852,6 +853,18 @@ def HLSLShader : InheritableAttr {
 
 // HLSL Change Ends
 
+// SPIRV Change Starts
+
+def VKLocation : InheritableAttr {
+  let Spellings = [CXX11<"vk", "location">];
+  let Subjects = SubjectList<[Function, ParmVar, Field], ErrorDiag>;
+  let Args = [IntArgument<"Number">];
+  let LangOpts = [SPIRV];
+  let Documentation = [Undocumented];
+}
+
+// SPIRV Change Ends
+
 def C11NoReturn : InheritableAttr {
   let Spellings = [Keyword<"_Noreturn">];
   let Subjects = SubjectList<[Function], ErrorDiag>;

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

@@ -158,6 +158,8 @@ public:
   bool IsHLSLLibrary;
   bool NoMinPrecision; // use strict precision, not min precision.
   // MS Change Ends
+
+  bool SPIRV = false;  // SPIRV Change
   
   bool isSignedOverflowDefined() const {
     return getSignedOverflowBehavior() == SOB_Defined;

+ 8 - 3
tools/clang/include/clang/Parse/Parser.h

@@ -2132,7 +2132,9 @@ private:
   IdentifierLoc *ParseIdentifierLoc();
 
   void MaybeParseCXX11Attributes(Declarator &D) {
-    if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+    if ((getLangOpts().CPlusPlus11 ||
+         getLangOpts().HLSL) && // HLSL change
+        isCXX11AttributeSpecifier()) {
       ParsedAttributesWithRange attrs(AttrFactory);
       SourceLocation endLoc;
       ParseCXX11Attributes(attrs, &endLoc);
@@ -2141,7 +2143,9 @@ private:
   }
   void MaybeParseCXX11Attributes(ParsedAttributes &attrs,
                                  SourceLocation *endLoc = nullptr) {
-    if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+    if ((getLangOpts().CPlusPlus11 ||
+         getLangOpts().HLSL) && // HLSL change
+        isCXX11AttributeSpecifier()) {
       ParsedAttributesWithRange attrsWithRange(AttrFactory);
       ParseCXX11Attributes(attrsWithRange, endLoc);
       attrs.takeAllFrom(attrsWithRange);
@@ -2150,7 +2154,8 @@ private:
   void MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
                                  SourceLocation *endLoc = nullptr,
                                  bool OuterMightBeMessageSend = false) {
-    if (getLangOpts().CPlusPlus11 &&
+    if ((getLangOpts().CPlusPlus11 ||
+         getLangOpts().HLSL) && // HLSL change
         isCXX11AttributeSpecifier(false, OuterMightBeMessageSend))
       ParseCXX11Attributes(attrs, endLoc);
   }

+ 1 - 2
tools/clang/lib/Parse/ParseDeclCXX.cpp

@@ -3817,7 +3817,6 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
 ///         identifier
 void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
                                           SourceLocation *endLoc) {
-  assert(!getLangOpts().HLSL && "unreachable code in HLSL, no parsing of C++11-style attributes"); // HLSL Change
   if (Tok.is(tok::kw_alignas)) {
     Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas);
     ParseAlignmentSpecifier(attrs, endLoc);
@@ -3898,7 +3897,7 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
 ///       attribute-specifier-seq[opt] attribute-specifier
 void Parser::ParseCXX11Attributes(ParsedAttributesWithRange &attrs,
                                   SourceLocation *endLoc) {
-  assert(getLangOpts().CPlusPlus11);
+  assert(getLangOpts().CPlusPlus11 || getLangOpts().HLSL); // HLSL Change
 
   SourceLocation StartLoc = Tok.getLocation(), Loc;
   if (!endLoc)

+ 0 - 6
tools/clang/lib/Parse/ParseTentative.cpp

@@ -498,12 +498,6 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
 Parser::CXX11AttributeKind
 Parser::isCXX11AttributeSpecifier(bool Disambiguate,
                                   bool OuterMightBeMessageSend) {
-  // HLSL Change Starts
-  if (getLangOpts().HLSL) {
-    return CAK_NotAttributeSpecifier;
-  }
-  // HLSL Change Ends
-
   if (Tok.is(tok::kw_alignas))
     return CAK_AttributeSpecifier;
 

+ 106 - 38
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -150,10 +150,12 @@ namespace {
 /// the same location.
 class LocationSet {
 public:
+  /// Maximum number of locations supported
   // Typically we won't have that many stage input or output variables.
   // Using 64 should be fine here.
-  // TODO: Emit errors if we need more than 64.
-  LocationSet() : usedLocs(64, false), nextLoc(0) {}
+  const static uint32_t kMaxLoc = 64;
+
+  LocationSet() : usedLocs(kMaxLoc, false), nextLoc(0) {}
 
   /// Uses the given location.
   void useLoc(uint32_t loc) { usedLocs.set(loc); }
@@ -166,72 +168,137 @@ public:
     return nextLoc++;
   }
 
+  /// Returns true if the given location number is already used.
+  bool isLocUsed(uint32_t loc) { return usedLocs[loc]; }
+
 private:
   llvm::SmallBitVector usedLocs; ///< All previously used locations
   uint32_t nextLoc;              ///< Next available location
 };
 } // namespace
 
-void DeclResultIdMapper::finalizeStageIOLocations() {
-  { // Check semantic duplication
-    llvm::StringSet<> seenInputSemantics;
-    llvm::StringSet<> seenOutputSemantics;
-    bool success = true;
+bool DeclResultIdMapper::checkSemanticDuplication(bool forInput) {
+  llvm::StringSet<> seenSemantics;
+  bool success = true;
+
+  for (const auto &var : stageVars) {
+    auto s = var.getSemanticStr();
+
+    if (forInput && var.getSigPoint()->IsInput()) {
+      if (seenSemantics.count(s)) {
+        emitError("input semantic '%0' used more than once") << s;
+        success = false;
+      }
+      seenSemantics.insert(s);
+    } else if (!forInput && var.getSigPoint()->IsOutput()) {
+      if (seenSemantics.count(s)) {
+        emitError("output semantic '%0' used more than once") << s;
+        success = false;
+      }
+      seenSemantics.insert(s);
+    }
+  }
+
+  return success;
+}
+
+bool DeclResultIdMapper::finalizeStageIOLocations(bool forInput) {
+  if (!checkSemanticDuplication(forInput))
+    return false;
+
+  // Returns false if the given StageVar is an input/output variable without
+  // explicit location assignment. Otherwise, returns true.
+  const auto locAssigned = [forInput](const StageVar &v) {
+    if (forInput ? v.getSigPoint()->IsInput() : v.getSigPoint()->IsOutput())
+      // No need to assign location for builtins. Treat as assigned.
+      return v.isSpirvBuitin() || v.getLocationAttr() != nullptr;
+    // For the ones we don't care, treat as assigned.
+    return true;
+  };
+
+  // If we have explicit location specified for all input/output variables,
+  // use them instead assign by ourselves.
+  if (std::all_of(stageVars.begin(), stageVars.end(), locAssigned)) {
+    LocationSet locSet;
+    bool noError = true;
 
     for (const auto &var : stageVars) {
-      auto s = var.getSemanticStr();
-      if (var.getSigPoint()->IsInput()) {
-        if (seenInputSemantics.count(s)) {
-          emitError("input semantic '%0' used more than once") << s;
-          success = false;
-        }
-        seenInputSemantics.insert(s);
-      } else {
-        if (seenOutputSemantics.count(s)) {
-          emitError("output semantic '%0' used more than once") << s;
-          success = false;
-        }
-        seenOutputSemantics.insert(s);
+      // Skip those stage variables we are not handling for this call
+      if ((forInput ? !var.getSigPoint()->IsInput()
+                    : !var.getSigPoint()->IsOutput()))
+        continue;
+
+      // Skip builtins
+      if (var.isSpirvBuitin())
+        continue;
+
+      const auto *attr = var.getLocationAttr();
+      const auto loc = attr->getNumber();
+      const auto attrLoc = attr->getLocation(); // Attr source code location
+
+      if (loc >= LocationSet::kMaxLoc) {
+        emitError("stage %select{output|input}0 location #%1 too large",
+                  attrLoc)
+            << forInput << loc;
+        return false;
       }
+
+      // Make sure the same location is not assigned more than once
+      if (locSet.isLocUsed(loc)) {
+        emitError("stage %select{output|input}0 location #%1 already assigned",
+                  attrLoc)
+            << forInput << loc;
+        noError = false;
+      }
+      locSet.useLoc(loc);
+
+      theBuilder.decorateLocation(var.getSpirvId(), loc);
     }
 
-    if (!success)
-      return;
+    return noError;
   }
 
-  std::vector<const StageVar *> inputVars;
-  std::vector<const StageVar *> outputVars;
+  std::vector<const StageVar *> vars;
+  LocationSet locSet;
 
-  LocationSet inputLocs;
-  LocationSet outputLocs;
+  for (const auto &var : stageVars) {
+    if ((forInput ? !var.getSigPoint()->IsInput()
+                  : !var.getSigPoint()->IsOutput()))
+      continue;
 
-  for (const auto &var : stageVars)
     if (!var.isSpirvBuitin()) {
+      if (var.getLocationAttr() != nullptr) {
+        // We have checked that not all of the stage variables have explicit
+        // location assignment.
+        emitError("partial explicit stage %select{output|input}0 location "
+                  "assignment via [[vk::location(X)]] unsupported")
+            << forInput;
+        return false;
+      }
+
       // Only SV_Target, SV_Depth, SV_DepthLessEqual, SV_DepthGreaterEqual,
       // SV_StencilRef, SV_Coverage are allowed in the pixel shader.
       // Arbitrary semantics are disallowed in pixel shader.
       if (var.getSemantic()->GetKind() == hlsl::Semantic::Kind::Target) {
         theBuilder.decorateLocation(var.getSpirvId(), var.getSemanticIndex());
-        outputLocs.useLoc(var.getSemanticIndex());
-      } else if (var.getSigPoint()->IsInput()) {
-        inputVars.push_back(&var);
-      } else if (var.getSigPoint()->IsOutput()) {
-        outputVars.push_back(&var);
+        locSet.useLoc(var.getSemanticIndex());
+      } else {
+        vars.push_back(&var);
       }
     }
+  }
 
   // Sort stage input/output variables alphabetically
   const auto comp = [](const StageVar *a, const StageVar *b) {
     return a->getSemanticStr() < b->getSemanticStr();
   };
 
-  std::sort(inputVars.begin(), inputVars.end(), comp);
-  std::sort(outputVars.begin(), outputVars.end(), comp);
+  std::sort(vars.begin(), vars.end(), comp);
+
+  for (const auto *var : vars)
+    theBuilder.decorateLocation(var->getSpirvId(), locSet.useNextLoc());
 
-  for (const auto *var : inputVars)
-    theBuilder.decorateLocation(var->getSpirvId(), inputLocs.useNextLoc());
-  for (const auto *var : outputVars)
-    theBuilder.decorateLocation(var->getSpirvId(), outputLocs.useNextLoc());
+  return true;
 }
 
 QualType
@@ -290,6 +357,7 @@ bool DeclResultIdMapper::createStageVars(const DeclaratorDecl *decl,
       return false;
 
     stageVar.setSpirvId(varId);
+    stageVar.setLocationAttr(decl->getAttr<VKLocationAttr>());
 
     stageVars.push_back(stageVar);
 

+ 29 - 9
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -16,6 +16,7 @@
 #include "dxc/HLSL/DxilShaderModel.h"
 #include "dxc/HLSL/DxilSigPoint.h"
 #include "spirv/1.0/spirv.hpp11"
+#include "clang/AST/Attr.h"
 #include "clang/SPIRV/ModuleBuilder.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/Optional.h"
@@ -52,8 +53,8 @@ public:
   spv::StorageClass getStorageClass() const { return storageClass; }
   void setStorageClass(spv::StorageClass sc) { storageClass = sc; }
 
-  bool hasLocation() const { return location.hasValue(); }
-  void setLocation(uint32_t loc) { location = llvm::Optional<uint32_t>(loc); }
+  const VKLocationAttr *getLocationAttr() const { return location; }
+  void setLocationAttr(const VKLocationAttr *loc) { location = loc; }
 
 private:
   /// HLSL SigPoint. It uniquely identifies each set of parameters that may be
@@ -74,7 +75,7 @@ private:
   /// SPIR-V storage class this stage variable belongs to.
   spv::StorageClass storageClass;
   /// Location assignment if input/output variable.
-  llvm::Optional<uint32_t> location;
+  const VKLocationAttr *location;
 };
 
 StageVar::StageVar(const hlsl::SigPoint *sig, llvm::StringRef semaStr,
@@ -82,7 +83,7 @@ StageVar::StageVar(const hlsl::SigPoint *sig, llvm::StringRef semaStr,
                    uint32_t type)
     : sigPoint(sig), semanticStr(semaStr), semantic(sema),
       semanticIndex(semaIndex), typeId(type), valueId(0), isBuiltin(false),
-      storageClass(spv::StorageClass::Max), location(llvm::None) {}
+      storageClass(spv::StorageClass::Max), location(nullptr) {}
 
 /// \brief The class containing mappings from Clang frontend Decls to their
 /// corresponding SPIR-V <result-id>s.
@@ -155,21 +156,35 @@ public:
   std::vector<uint32_t> collectStageVars() const;
 
   /// \brief Decorates all stage input and output variables with proper
-  /// location.
+  /// location and returns true on success.
   ///
-  /// This method will writes the location assignment into the module under
+  /// This method will write the location assignment into the module under
   /// construction.
-  void finalizeStageIOLocations();
+  inline bool decorateStageIOLocations();
 
 private:
   /// \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]) {
+  template <unsigned N>
+  DiagnosticBuilder emitError(const char (&message)[N],
+                              SourceLocation loc = {}) {
     const auto diagId =
         diags.getCustomDiagID(clang::DiagnosticsEngine::Error, message);
-    return diags.Report(diagId);
+    return diags.Report(loc, diagId);
   }
 
+  /// \brief Checks whether some semantic is used more than once and returns
+  /// true if no such cases. Returns false otherwise.
+  bool checkSemanticDuplication(bool forInput);
+
+  /// \brief Decorates all stage input (if forInput is true) or output (if
+  /// forInput is false) variables with proper location and returns true on
+  /// success.
+  ///
+  /// This method will write the location assignment into the module under
+  /// 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;
@@ -210,6 +225,11 @@ DeclResultIdMapper::DeclResultIdMapper(const hlsl::ShaderModel &model,
     : shaderModel(model), theBuilder(builder), typeTranslator(builder, diag),
       diags(diag), entryFunctionId(0) {}
 
+bool DeclResultIdMapper::decorateStageIOLocations() {
+  // Try both input and output even if input location assignment failed
+  return finalizeStageIOLocations(true) & finalizeStageIOLocations(false);
+}
+
 } // end namespace spirv
 } // end namespace clang
 

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

@@ -182,7 +182,8 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
   AddExecutionModeForEntryPoint(entryFunctionId);
 
   // Add Location decorations to stage input/output variables.
-  declIdMapper.finalizeStageIOLocations();
+  if (!declIdMapper.decorateStageIOLocations())
+    return;
 
   // Output the constructed module.
   std::vector<uint32_t> m = theBuilder.takeModule();

+ 23 - 1
tools/clang/lib/Sema/SemaHLSL.cpp

@@ -10146,7 +10146,7 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
 	  break;
   default:
     Handled = false;
-    return;
+    break;  // SPIRV Change: was return;
   }
 
   if (declAttr != nullptr)
@@ -10157,7 +10157,29 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,
     // The attribute has been set but will have no effect. Validation will emit a diagnostic
     // and prevent code generation.
     ValidateAttributeTargetIsFunction(S, D, A);
+
+    return; // SPIRV Change
+  }
+
+  // SPIRV Change Starts
+  Handled = true;
+  switch (A.getKind())
+  {
+  case AttributeList::AT_VKLocation:
+	  declAttr = ::new (S.Context) VKLocationAttr(A.getRange(), S.Context,
+		  ValidateAttributeIntArg(S, A), A.getAttributeSpellingListIndex());
+	  break;
+  default:
+    Handled = false;
+    return;
+  }
+
+  if (declAttr != nullptr)
+  {
+    DXASSERT_NOMSG(Handled);
+    D->addAttr(declAttr);
   }
+  // SPIRV Change Ends
 }
 
 /// <summary>Processes an attribute for a statement.</summary>

+ 1 - 1
tools/clang/test/CodeGenSPIRV/passthru-ps.hlsl2spv

@@ -22,8 +22,8 @@ float4 main(float4 input: COLOR): SV_Target
 // OpName %out_var_SV_Target "out.var.SV_Target"
 // OpName %src_main "src.main"
 // OpName %input "input"
-// OpDecorate %out_var_SV_Target Location 0
 // OpDecorate %in_var_COLOR Location 0
+// OpDecorate %out_var_SV_Target Location 0
 // %void = OpTypeVoid
 // %3 = OpTypeFunction %void
 // %float = OpTypeFloat 32

+ 34 - 0
tools/clang/test/CodeGenSPIRV/vk.location.exp-in.hlsl

@@ -0,0 +1,34 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct S {
+    [[vk::location(2)]] int    a: A; // In nested struct --- A -> 2
+};
+
+struct T {
+    [[vk::location(1)]] float4 b: B; // In struct --- B -> 1
+                        S      s;
+};
+
+struct U {
+                        float4 c: C;
+};
+
+float main([[vk::location(0)]] in  uint   m: M, // On function parameter --- M -> 0
+                                   T      t,
+                               out float  n: N,
+                               out U      u,
+
+                               out float4 pos: SV_Position
+          ) : R {
+    return 1.0;
+}
+
+// Explicit assignment
+// CHECK: OpDecorate %in_var_M Location 0
+// CHECK: OpDecorate %in_var_B Location 1
+// CHECK: OpDecorate %in_var_A Location 2
+
+// Alphabetical assignment
+// CHECK: OpDecorate %out_var_C Location 0
+// CHECK: OpDecorate %out_var_N Location 1
+// CHECK: OpDecorate %out_var_R Location 2

+ 36 - 0
tools/clang/test/CodeGenSPIRV/vk.location.exp-out.hlsl

@@ -0,0 +1,36 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct S {
+                        int    a: A;
+};
+
+struct T {
+                        float4 b: B;
+                        S      s;
+};
+
+struct U {
+    [[vk::location(1)]] float4 c: C; // In struct --- C -> 1
+};
+
+[[vk::location(2)]]                  // On function return --- R -> 2
+float main(                    in  uint   m: M,
+                                   T      t,
+           [[vk::location(0)]] out float  n: N, // On function parameter --- N -> 0
+                               out U      u,
+
+                               out float4 pos: SV_Position
+          ) : R {
+    return 1.0;
+}
+
+// Alphabetical assignment
+// CHECK: OpDecorate %in_var_A Location 0
+// CHECK: OpDecorate %in_var_B Location 1
+// CHECK: OpDecorate %in_var_M Location 2
+
+// Explicit assignment
+// CHECK: OpDecorate %out_var_R Location 2
+// CHECK: OpDecorate %out_var_N Location 0
+// CHECK: OpDecorate %out_var_C Location 1
+

+ 35 - 0
tools/clang/test/CodeGenSPIRV/vk.location.hlsl

@@ -0,0 +1,35 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct S {
+    [[vk::location(2)]] int    a: A; // In nested struct --- A -> 2
+};
+
+struct T {
+    [[vk::location(1)]] float4 b: B; // In struct --- B -> 1
+                        S      s;
+};
+
+struct U {
+    [[vk::location(1)]] float4 c: C; // In struct --- C -> 1
+};
+
+[[vk::location(2)]]                  // On function return --- R -> 2
+float main([[vk::location(0)]] in  uint   m: M, // On function parameter --- M -> 0
+                                   T      t,
+           [[vk::location(0)]] out float  n: N, // On function parameter --- N -> 0
+                               out U      u,
+
+                               out float4 pos: SV_Position
+          ) : R {
+    return 1.0;
+}
+
+// Explicit assignment
+// CHECK: OpDecorate %in_var_M Location 0
+// CHECK: OpDecorate %in_var_B Location 1
+// CHECK: OpDecorate %in_var_A Location 2
+
+// Explicit assignment
+// CHECK: OpDecorate %out_var_R Location 2
+// CHECK: OpDecorate %out_var_N Location 0
+// CHECK: OpDecorate %out_var_C Location 1

+ 6 - 0
tools/clang/test/CodeGenSPIRV/vk.location.large.hlsl

@@ -0,0 +1,6 @@
+// Run: %dxc -T vs_6_0 -E main
+
+[[vk::location(123456)]]
+float main() : A { return 1.0; }
+
+// CHECK: 3:3: error: stage output location #123456 too large

+ 10 - 0
tools/clang/test/CodeGenSPIRV/vk.location.mixed.hlsl

@@ -0,0 +1,10 @@
+// Run: %dxc -T vs_6_0 -E main
+
+float main(
+    [[vk::location(5)]] int   a: A,
+                        float b: B
+) : R { return 1.0; }
+
+// CHECK:      error: partial explicit stage input location assignment via
+// CHECK-SAME: vk::location(X)
+// CHECK-SAME: unsupported

+ 33 - 0
tools/clang/test/CodeGenSPIRV/vk.location.reassign.hlsl

@@ -0,0 +1,33 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct S {
+    [[vk::location(3)]] float a : A;  // error
+    [[vk::location(3)]] float b : B;  // error
+};
+
+struct T {
+    [[vk::location(3)]] float i : A;  // error
+    [[vk::location(3)]] float j : B;  // error
+};
+
+[[vk::location(3)]]                   // first use
+float main(
+    [[vk::location(3)]]     float m : M,  // first use
+    [[vk::location(3)]]     float n : N,  // error
+                            S     s,
+
+    [[vk::location(3)]] out float x : X,  // error
+    [[vk::location(3)]] out float y : Y,  // error
+                        out T     t
+) : R {
+    return 1.0;
+}
+
+// CHECK: 16:7: error: stage input location #3 already assigned
+// CHECK: 4:7: error: stage input location #3 already assigned
+// CHECK: 5:7: error: stage input location #3 already assigned
+
+// CHECK: 19:7: error: stage output location #3 already assigned
+// CHECK: 20:7: error: stage output location #3 already assigned
+// CHECK: 9:7: error: stage output location #3 already assigned
+// CHECK: 10:7: error: stage output location #3 already assigned

+ 13 - 0
tools/clang/test/HLSL/cxx11-attributes.hlsl

@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -fsyntax-only -ffreestanding -verify %s
+
+struct S {
+    [[vk::location(1)]] // expected-warning {{'location' attribute ignored}}
+    float a: A;
+};
+
+[[maybe_unused]] // expected-warning {{unknown attribute 'maybe_unused' ignored}}
+float main([[scope::attr(0, "str")]] // expected-warning {{unknown attribute 'attr' ignored}}
+           float m: B,
+           S s) : C {
+    return m + s.a;
+}

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

@@ -789,6 +789,12 @@ public:
 
     compiler.getLangOpts().NoMinPrecision = Opts.NoMinPrecision;
 
+// SPIRV change starts
+#ifdef ENABLE_SPIRV_CODEGEN
+    compiler.getLangOpts().SPIRV = Opts.GenSPIRV;
+#endif
+// SPIRV change ends
+
     if (Opts.WarningAsError)
       compiler.getDiagnostics().setWarningsAsErrors(true);
 

+ 5 - 0
tools/clang/unittests/HLSL/VerifierTest.cpp

@@ -40,6 +40,7 @@ public:
   TEST_METHOD(RunConstAssign);
   TEST_METHOD(RunConstDefault);
   TEST_METHOD(RunCppErrors);
+  TEST_METHOD(RunCXX11Attributes);
   TEST_METHOD(RunEnums);
   TEST_METHOD(RunFunctions);
   TEST_METHOD(RunIndexingOperator);
@@ -163,6 +164,10 @@ TEST_F(VerifierTest, RunCppErrors) {
   CheckVerifiesHLSL(L"cpp-errors.hlsl");
 }
 
+TEST_F(VerifierTest, RunCXX11Attributes) {
+  CheckVerifiesHLSL(L"cxx11-attributes.hlsl");
+}
+
 TEST_F(VerifierTest, RunEnums) {
   CheckVerifiesHLSL(L"enums.hlsl");
 }

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

@@ -350,5 +350,21 @@ TEST_F(FileTest, SpirvEntryFunctionWrapper) {
 TEST_F(FileTest, SpirvEntryFunctionInOut) {
   runFileTest("spirv.entry-function.inout.hlsl");
 }
+TEST_F(FileTest, VulkanLocation) { runFileTest("vk.location.hlsl"); }
+TEST_F(FileTest, VulkanLocationInputExplicitOutputImplicit) {
+  runFileTest("vk.location.exp-in.hlsl");
+}
+TEST_F(FileTest, VulkanLocationInputImplicitOutputExplicit) {
+  runFileTest("vk.location.exp-out.hlsl");
+}
+TEST_F(FileTest, VulkanLocationTooLarge) {
+  runFileTest("vk.location.large.hlsl", /*expectSuccess*/ false);
+}
+TEST_F(FileTest, VulkanLocationReassigned) {
+  runFileTest("vk.location.reassign.hlsl", /*expectSuccess*/ false);
+}
+TEST_F(FileTest, VulkanLocationPartiallyAssigned) {
+  runFileTest("vk.location.mixed.hlsl", /*expectSuccess*/ false);
+}
 
 } // namespace

+ 13 - 0
tools/clang/utils/TableGen/ClangAttrEmitter.cpp

@@ -1924,8 +1924,21 @@ static void GenerateHasAttrSpellingStringSwitch(
     } else if (Variety == "CXX11")
       // C++11 mode should be checked against LangOpts, which is presumed to be
       // present in the caller.
+    {  // SPIRV Change
       Test = "LangOpts.CPlusPlus11";
 
+      // SPIRV Change Begins
+      // Allow C++11 attribute specifiers in HLSL when they are of the
+      // vk namespace
+      std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);
+      for (const auto &S : Spellings)
+        if (S.nameSpace() == "vk") {
+          Test = "(LangOpts.HLSL || LangOpts.CPlusPlus11)";
+          break;
+        }
+    }
+    // SPIRV Change Ends
+
     std::string TestStr =
         !Test.empty() ? Test + " ? " + llvm::itostr(Version) + " : 0" : "1";
     std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);