Browse Source

[spirv] Allow aliasing builtin variables (#1691)

This behavior is supported by FXC.

Fixes https://github.com/Microsoft/DirectXShaderCompiler/issues/1690
Lei Zhang 6 years ago
parent
commit
de62ed4fe3

+ 13 - 0
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -524,6 +524,19 @@ private:
   InstBuilder instBuilder;
   std::vector<uint32_t> constructSite; ///< InstBuilder construction site.
   uint32_t glslExtSetId; ///< The <result-id> of GLSL extended instruction set.
+
+  /// A struct containing information regarding a builtin variable.
+  struct BuiltInVarInfo {
+    BuiltInVarInfo(spv::StorageClass s, spv::BuiltIn b, uint32_t v)
+        : sc(s), builtIn(b), variable(v) {}
+
+    spv::StorageClass sc;
+    spv::BuiltIn builtIn;
+    uint32_t variable;
+  };
+
+  /// Used as caches for all created builtin variables to avoid duplication.
+  llvm::SmallVector<BuiltInVarInfo, 16> builtinVars;
 };
 
 SPIRVContext *ModuleBuilder::getSPIRVContext() { return &theContext; }

+ 13 - 2
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -873,8 +873,14 @@ std::vector<uint32_t> DeclResultIdMapper::collectStageVars() const {
   for (auto var : glPerVertex.getStageOutVars())
     vars.push_back(var);
 
-  for (const auto &var : stageVars)
-    vars.push_back(var.getSpirvId());
+  llvm::DenseSet<uint32_t> seenVars;
+  for (const auto &var : stageVars) {
+    const auto id = var.getSpirvId();
+    if (seenVars.count(id) == 0) {
+      vars.push_back(id);
+      seenVars.insert(id);
+    }
+  }
 
   return vars;
 }
@@ -974,6 +980,11 @@ bool DeclResultIdMapper::checkSemanticDuplication(bool forInput) {
       continue;
     }
 
+    // Allow builtin variables to alias each other. We already have uniqify
+    // mechanism in ModuleBuilder.
+    if (var.isSpirvBuitin())
+      continue;
+
     if (forInput && var.getSigPoint()->IsInput()) {
       if (seenSemantics.count(s)) {
         emitError("input semantic '%0' used more than once", {}) << s;

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

@@ -762,6 +762,15 @@ uint32_t ModuleBuilder::addStageIOVar(uint32_t type,
 
 uint32_t ModuleBuilder::addStageBuiltinVar(uint32_t type, spv::StorageClass sc,
                                            spv::BuiltIn builtin) {
+  auto found =
+      std::find_if(builtinVars.begin(), builtinVars.end(),
+                   [sc, builtin](const BuiltInVarInfo &varInfo) {
+                     return varInfo.sc == sc && varInfo.builtIn == builtin;
+                   });
+  if (found != builtinVars.end()) {
+    return found->variable;
+  }
+
   const uint32_t pointerType = getPointerType(type, sc);
   const uint32_t varId = theContext.takeNextId();
   instBuilder.opVariable(pointerType, varId, sc, llvm::None).x();
@@ -771,6 +780,8 @@ uint32_t ModuleBuilder::addStageBuiltinVar(uint32_t type, spv::StorageClass sc,
   const Decoration *d = Decoration::getBuiltIn(theContext, builtin);
   theModule.addDecoration(d, varId);
 
+  builtinVars.emplace_back(sc, builtin, varId);
+
   return varId;
 }
 

+ 21 - 0
tools/clang/test/CodeGenSPIRV/spirv.interface.alias-builtin.hlsl

@@ -0,0 +1,21 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: OpEntryPoint Fragment %main "main" %gl_FragCoord %out_var_SV_Target
+
+// CHECK:     %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+// CHECK-NOT:      {{%\d+}} = OpVariable %_ptr_Input_v4float Input
+
+struct PSInput {
+  float4 a : SV_Position;
+  float4 b : SV_Position;
+};
+
+// CHECK:      [[a:%\d+]] = OpLoad %v4float %gl_FragCoord
+// CHECK-NEXT: [[b:%\d+]] = OpLoad %v4float %gl_FragCoord
+// CHECK-NEXT:   {{%\d+}} = OpCompositeConstruct %PSInput [[a]] [[b]]
+
+float4 main(PSInput input) : SV_Target
+{
+	return input.a + input.b;
+}
+

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

@@ -1294,6 +1294,10 @@ TEST_F(FileTest, SpirvStageIOInterfacePS) {
   runFileTest("spirv.interface.ps.hlsl");
 }
 
+TEST_F(FileTest, SpirvStageIOAliasBuiltIn) {
+  runFileTest("spirv.interface.alias-builtin.hlsl");
+}
+
 TEST_F(FileTest, SpirvStageIO16bitTypes) {
   runFileTest("spirv.stage-io.16bit.hlsl");
 }