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

[spirv] Propagate RelaxedPrecision decoration. (#1915)

* [spirv] Propagate RelaxedPrecision decoration.

* Address issues brought up in issue #1736
Ehsan 6 жил өмнө
parent
commit
af46b67b6c

+ 0 - 4
tools/clang/include/clang/SPIRV/SpirvBuilder.h

@@ -535,10 +535,6 @@ public:
   /// \brief Decorates the given target with block
   void decorateBlock(SpirvInstruction *target, SourceLocation srcLoc = {});
 
-  /// \brief Decorates the given target with relaxedprecision
-  void decorateRelaxedPrecision(SpirvInstruction *target,
-                                SourceLocation srcLoc = {});
-
   /// \brief Decorates the given target with patch
   void decoratePatch(SpirvInstruction *target, SourceLocation srcLoc = {});
 

+ 7 - 0
tools/clang/include/clang/SPIRV/SpirvFunction.h

@@ -56,6 +56,11 @@ public:
   // Returns the SPIR-V type of the function
   SpirvType *getFunctionType() const { return fnType; }
 
+  // Store that the return type is at relaxed precision.
+  void setRelaxedPrecision() { relaxedPrecision = true; }
+  // Returns whether the return type has relaxed precision.
+  uint32_t isRelaxedPrecision() const { return relaxedPrecision; }
+
   void setSourceLocation(SourceLocation loc) { functionLoc = loc; }
   SourceLocation getSourceLocation() const { return functionLoc; }
 
@@ -83,6 +88,8 @@ private:
   SpirvType *returnType;  ///< The lowered return type
   SpirvType *fnType;      ///< The SPIR-V function type
 
+  bool relaxedPrecision; ///< Whether the return type is at relaxed precision
+
   /// Legalization-specific code
   ///
   /// Note: the following two member variables are currently needed in order to

+ 6 - 2
tools/clang/include/clang/SPIRV/SpirvType.h

@@ -290,9 +290,11 @@ public:
     FieldInfo(const SpirvType *type_, llvm::StringRef name_ = "",
               llvm::Optional<uint32_t> offset_ = llvm::None,
               llvm::Optional<uint32_t> matrixStride_ = llvm::None,
-              llvm::Optional<bool> isRowMajor_ = llvm::None)
+              llvm::Optional<bool> isRowMajor_ = llvm::None,
+              bool relaxedPrecision = false)
         : type(type_), name(name_), offset(offset_),
-          matrixStride(matrixStride_), isRowMajor(isRowMajor_) {
+          matrixStride(matrixStride_), isRowMajor(isRowMajor_),
+          isRelaxedPrecision(relaxedPrecision) {
       // A StructType may not contain any hybrid types.
       assert(!isa<HybridType>(type_));
     }
@@ -309,6 +311,8 @@ public:
     llvm::Optional<uint32_t> matrixStride;
     // The majorness of this field (if applicable).
     llvm::Optional<bool> isRowMajor;
+    // Whether this field is a RelaxedPrecision field.
+    bool isRelaxedPrecision;
   };
 
   StructType(

+ 19 - 0
tools/clang/lib/SPIRV/AstTypeProbe.cpp

@@ -879,6 +879,9 @@ bool isOpaqueArrayType(QualType type) {
 }
 
 bool isRelaxedPrecisionType(QualType type, const SpirvCodeGenOptions &opts) {
+  if (type.isNull())
+    return false;
+
   // Primitive types
   {
     QualType ty = {};
@@ -911,6 +914,22 @@ bool isRelaxedPrecisionType(QualType type, const SpirvCodeGenOptions &opts) {
       return isRelaxedPrecisionType(elemType, opts);
   }
 
+  // Images with RelaxedPrecision sampled type.
+  if (const auto *recordType = type->getAs<RecordType>()) {
+    const llvm::StringRef name = recordType->getDecl()->getName();
+    if (name == "Texture1D" || name == "Texture2D" || name == "Texture3D" ||
+        name == "TextureCube" || name == "Texture1DArray" ||
+        name == "Texture2DArray" || name == "Texture2DMS" ||
+        name == "Texture2DMSArray" || name == "TextureCubeArray" ||
+        name == "RWTexture1D" || name == "RWTexture2D" ||
+        name == "RWTexture3D" || name == "RWTexture1DArray" ||
+        name == "RWTexture2DArray" || name == "Buffer" || name == "RWBuffer" ||
+        name == "SubpassInput" || name == "SubpassInputMS") {
+      const auto sampledType = hlsl::GetHLSLResourceResultType(type);
+      return isRelaxedPrecisionType(sampledType, opts);
+    }
+  }
+
   return false;
 }
 

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

@@ -15,6 +15,7 @@ add_clang_library(clangSPIRV
   InitListHandler.cpp
   LiteralTypeVisitor.cpp
   LowerTypeVisitor.cpp
+  RelaxedPrecisionVisitor.cpp
   SpirvBasicBlock.cpp
   SpirvBuilder.cpp
   SpirvContext.cpp

+ 14 - 0
tools/clang/lib/SPIRV/EmitVisitor.cpp

@@ -94,6 +94,11 @@ void EmitVisitor::initInstruction(SpirvInstruction *inst) {
     typeHandler.emitDecoration(getOrAssignResultId<SpirvInstruction>(inst),
                                spv::Decoration::NonUniformEXT, {});
   }
+  // Emit RelaxedPrecision decoration (if any).
+  if (inst->isRelaxedPrecision()) {
+    typeHandler.emitDecoration(getOrAssignResultId<SpirvInstruction>(inst),
+                               spv::Decoration::RelaxedPrecision, {});
+  }
 
   // Initialize the current instruction for emitting.
   curInst.clear();
@@ -199,6 +204,11 @@ bool EmitVisitor::visit(SpirvFunction *fn, Phase phase) {
     finalizeInstruction();
     emitDebugNameForInstruction(getOrAssignResultId<SpirvFunction>(fn),
                                 fn->getFunctionName());
+
+    // RelaxedPrecision decoration may be applied to an OpFunction instruction.
+    if (fn->isRelaxedPrecision())
+      typeHandler.emitDecoration(getOrAssignResultId<SpirvFunction>(fn),
+                                 spv::Decoration::RelaxedPrecision, {});
   }
   // After emitting the function
   else if (phase == Visitor::Phase::Done) {
@@ -1440,6 +1450,10 @@ uint32_t EmitTypeHandler::emitType(const SpirvType *type) {
                                                    : spv::Decoration::ColMajor,
                        {}, i);
 
+      // RelaxedPrecision decorations
+      if (field.isRelaxedPrecision)
+        emitDecoration(id, spv::Decoration::RelaxedPrecision, {}, i);
+
       // NonWritable decorations
       if (structType->isReadOnly())
         emitDecoration(id, spv::Decoration::NonWritable, {}, i);

+ 5 - 0
tools/clang/lib/SPIRV/LowerTypeVisitor.cpp

@@ -723,6 +723,11 @@ LowerTypeVisitor::populateLayoutInformation(
     StructType::FieldInfo loweredField(
         lowerType(fieldType, rule, /*isRowMajor*/ llvm::None, {}), field.name);
 
+    // Set RelaxedPrecision information for the lowered field.
+    if (isRelaxedPrecisionType(fieldType, spvOptions)) {
+      loweredField.isRelaxedPrecision = true;
+    }
+
     // We only need layout information for strcutres with non-void layout rule.
     if (rule == SpirvLayoutRule::Void) {
       loweredFields.push_back(loweredField);

+ 250 - 0
tools/clang/lib/SPIRV/RelaxedPrecisionVisitor.cpp

@@ -0,0 +1,250 @@
+//===--- RelaxedPrecisionVisitor.cpp - RelaxedPrecision Visitor --*- C++ -*-==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RelaxedPrecisionVisitor.h"
+#include "clang/SPIRV/AstTypeProbe.h"
+#include "clang/SPIRV/SpirvBuilder.h"
+
+namespace clang {
+namespace spirv {
+
+bool RelaxedPrecisionVisitor::visit(SpirvFunction *fn, Phase phase) {
+  assert(fn);
+  if (phase == Visitor::Phase::Init)
+    if (isRelaxedPrecisionType(fn->getAstReturnType(), spvOptions))
+      fn->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvVectorShuffle *inst) {
+  // The result of vector shuffle must have RelaxedPrecision if the chosen
+  // elements come from a vector that is RelaxedPrecision.
+  auto *vec1 = inst->getVec1();
+  auto *vec2 = inst->getVec2();
+  const auto vec1Type = vec1->getAstResultType();
+  const auto vec2Type = vec2->getAstResultType();
+  const bool isVec1Relaxed = isRelaxedPrecisionType(vec1Type, spvOptions);
+  const bool isVec2Relaxed = isRelaxedPrecisionType(vec2Type, spvOptions);
+  uint32_t vec1Size;
+  uint32_t vec2Size;
+  (void)isVectorType(vec1Type, nullptr, &vec1Size);
+  (void)isVectorType(vec2Type, nullptr, &vec2Size);
+  bool vec1ElemUsed = false;
+  bool vec2ElemUsed = false;
+  for (auto component : inst->getComponents()) {
+    if (component < vec1Size)
+      vec1ElemUsed = true;
+    else
+      vec2ElemUsed = true;
+  }
+  const bool onlyVec1Used = vec1ElemUsed && !vec2ElemUsed;
+  const bool onlyVec2Used = vec2ElemUsed && !vec1ElemUsed;
+  if ((onlyVec1Used && isVec1Relaxed) || (onlyVec2Used && isVec2Relaxed) ||
+      (vec1ElemUsed && vec2ElemUsed && isVec1Relaxed && isVec2Relaxed))
+    inst->setRelaxedPrecision();
+
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvUnaryOp *inst) {
+  // For conversion operations, check the result QualType. For example: if we
+  // are converting from min12int to int, the result should no longet get
+  // RelaxedPrecision.
+  switch (inst->getopcode()) {
+  case spv::Op::OpBitcast:
+  case spv::Op::OpFConvert:
+  case spv::Op::OpSConvert:
+  case spv::Op::OpUConvert: {
+    if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions)) {
+      inst->setRelaxedPrecision();
+    }
+    return true;
+  }
+  default:
+    break;
+  }
+
+  // If the argument of the unary operation is RelaxedPrecision, the result is
+  // also RelaxedPrecision.
+  if (inst->getOperand()->isRelaxedPrecision())
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvBinaryOp *inst) {
+  // If either argument of the binary operation is RelaxedPrecision, the result
+  // is also RelaxedPrecision.
+  if (inst->getOperand1()->isRelaxedPrecision() &&
+      inst->getOperand2()->isRelaxedPrecision())
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvSpecConstantUnaryOp *inst) {
+  // If the argument of the unary operation is RelaxedPrecision, the result is
+  // also RelaxedPrecision.
+  if (inst->getOperand()->isRelaxedPrecision())
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvSpecConstantBinaryOp *inst) {
+  // If either argument of the binary operation is RelaxedPrecision, the result
+  // is also RelaxedPrecision.
+  if (inst->getOperand1()->isRelaxedPrecision() &&
+      inst->getOperand2()->isRelaxedPrecision())
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvLoad *inst) {
+  // If loading from a RelaxedPrecision variable, the result is also decorated
+  // with RelaxedPrecision.
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvStore *inst) { return true; }
+
+bool RelaxedPrecisionVisitor::visit(SpirvSelect *inst) {
+  if (inst->getTrueObject()->isRelaxedPrecision() &&
+      inst->getFalseObject()->isRelaxedPrecision())
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvFunctionCall *inst) {
+  // If the return type of the function is RelaxedPrecision, we can decorate the
+  // result-id of the OpFunctionCall.
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvExtInst *inst) {
+  // If all operands to numeric instructions in GLSL extended instruction set is
+  // RelaxedPrecision, the result of the opration is also RelaxedPrecision.
+  if (inst->getInstructionSet()->getExtendedInstSetName() == "GLSL.std.450") {
+    const auto &operands = inst->getOperands();
+    if (std::all_of(operands.begin(), operands.end(), [](SpirvInstruction *op) {
+          return op->isRelaxedPrecision();
+        })) {
+      inst->setRelaxedPrecision();
+    }
+  }
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvCompositeInsert *inst) {
+  // If inserting a RelaxedPrecision object into a composite, check the type of
+  // the resulting composite. For example: if you are inserting a
+  // RelaxedPrecision object as a member into a structure, the resulting
+  // structure type is not RelaxedPrecision. But, if you are inserting a
+  // RelaxedPrecision object into a vector of RelaxedPrecision integers, the
+  // resulting composite *is* RelaxedPrecision.
+  // In short, it simply depends on the composite type.
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvCompositeExtract *inst) {
+  // If extracting a RelaxedPrecision object from a composite, check the type of
+  // the extracted object. For example: if extracting different members of a
+  // structure, depending on the member, you may or may not want to apply the
+  // RelaxedPrecision decoration.
+  // In short, it simply depends on the type of what you have extracted.
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvCompositeConstruct *inst) {
+  // When constructing a composite, only look at the type of the resulting
+  // composite.
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvConstantBoolean *) {
+  // Booleans do not have precision!
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvConstantInteger *inst) {
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvConstantFloat *inst) {
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvConstantComposite *inst) {
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvBitFieldExtract *inst) {
+  if (inst->getBase()->isRelaxedPrecision())
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvBitFieldInsert *inst) {
+  if (inst->getBase()->isRelaxedPrecision())
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvAtomic *inst) {
+  // If the original pointer is RelaxedPrecision or operating on a value that is
+  // RelaxedPrecision, result is RelaxedPrecision.
+  if (inst->getPointer()->isRelaxedPrecision()) {
+    if (!inst->hasValue() ||
+        (inst->hasValue() && inst->getValue()->isRelaxedPrecision()))
+      inst->setRelaxedPrecision();
+  }
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvAccessChain *) {
+  // The access chain operation itself is irrelevant regarding precision.
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvFunctionParameter *inst) {
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvVariable *inst) {
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+bool RelaxedPrecisionVisitor::visit(SpirvImageOp *inst) {
+  // If the operation result type or the underlying image type is relaxed
+  // precision, the instruction can be considered relaxed precision.
+  if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions) ||
+      isRelaxedPrecisionType(inst->getImage()->getAstResultType(), spvOptions))
+    inst->setRelaxedPrecision();
+  return true;
+}
+
+} // end namespace spirv
+} // namespace clang

+ 62 - 0
tools/clang/lib/SPIRV/RelaxedPrecisionVisitor.h

@@ -0,0 +1,62 @@
+//===--- RelaxedPrecisionVisitor.h - RelaxedPrecision Visitor ----*- 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_RELAXEDPRECISIONVISITOR_H
+#define LLVM_CLANG_LIB_SPIRV_RELAXEDPRECISIONVISITOR_H
+
+#include "clang/SPIRV/SpirvContext.h"
+#include "clang/SPIRV/SpirvVisitor.h"
+
+namespace clang {
+namespace spirv {
+
+class RelaxedPrecisionVisitor : public Visitor {
+public:
+  RelaxedPrecisionVisitor(SpirvContext &spvCtx, const SpirvCodeGenOptions &opts)
+      : Visitor(opts, spvCtx) {}
+
+  bool visit(SpirvFunction *, Phase);
+
+  bool visit(SpirvVariable *);
+  bool visit(SpirvFunctionParameter *);
+  bool visit(SpirvAccessChain *);
+  bool visit(SpirvAtomic *);
+  bool visit(SpirvBitFieldExtract *);
+  bool visit(SpirvBitFieldInsert *);
+  bool visit(SpirvConstantBoolean *);
+  bool visit(SpirvConstantInteger *);
+  bool visit(SpirvConstantFloat *);
+  bool visit(SpirvConstantComposite *);
+  bool visit(SpirvCompositeConstruct *);
+  bool visit(SpirvCompositeExtract *);
+  bool visit(SpirvCompositeInsert *);
+  bool visit(SpirvExtInst *);
+  bool visit(SpirvFunctionCall *);
+  bool visit(SpirvLoad *);
+  bool visit(SpirvSelect *);
+  bool visit(SpirvStore *);
+  bool visit(SpirvSpecConstantBinaryOp *);
+  bool visit(SpirvSpecConstantUnaryOp *);
+  bool visit(SpirvBinaryOp *);
+  bool visit(SpirvUnaryOp *);
+  bool visit(SpirvVectorShuffle *);
+  bool visit(SpirvImageOp *);
+
+  /// The "sink" visit function for all instructions.
+  ///
+  /// By default, all other visit instructions redirect to this visit function.
+  /// So that you want override this visit function to handle all instructions,
+  /// regardless of their polymorphism.
+  bool visitInstruction(SpirvInstruction *instr) { return true; }
+};
+
+} // end namespace spirv
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_SPIRV_RELAXEDPRECISIONVISITOR_H

+ 5 - 7
tools/clang/lib/SPIRV/SpirvBuilder.cpp

@@ -12,6 +12,7 @@
 #include "EmitVisitor.h"
 #include "LiteralTypeVisitor.h"
 #include "LowerTypeVisitor.h"
+#include "RelaxedPrecisionVisitor.h"
 #include "clang/SPIRV/AstTypeProbe.h"
 
 namespace clang {
@@ -994,13 +995,6 @@ void SpirvBuilder::decorateBlock(SpirvInstruction *target,
   module->addDecoration(decor);
 }
 
-void SpirvBuilder::decorateRelaxedPrecision(SpirvInstruction *target,
-                                            SourceLocation srcLoc) {
-  auto *decor = new (context)
-      SpirvDecoration(srcLoc, target, spv::Decoration::RelaxedPrecision);
-  module->addDecoration(decor);
-}
-
 void SpirvBuilder::decoratePatch(SpirvInstruction *target,
                                  SourceLocation srcLoc) {
   auto *decor =
@@ -1063,6 +1057,7 @@ std::vector<uint32_t> SpirvBuilder::takeModule() {
   LiteralTypeVisitor literalTypeVisitor(astContext, context, spirvOptions);
   LowerTypeVisitor lowerTypeVisitor(astContext, context, spirvOptions);
   CapabilityVisitor capabilityVisitor(astContext, context, spirvOptions, *this);
+  RelaxedPrecisionVisitor relaxedPrecisionVisitor(context, spirvOptions);
   EmitVisitor emitVisitor(astContext, context, spirvOptions);
 
   module->invokeVisitor(&literalTypeVisitor, true);
@@ -1073,6 +1068,9 @@ std::vector<uint32_t> SpirvBuilder::takeModule() {
   // Add necessary capabilities and extensions
   module->invokeVisitor(&capabilityVisitor);
 
+  // Propagate RelaxedPrecision decorations
+  module->invokeVisitor(&relaxedPrecisionVisitor);
+
   // Emit SPIR-V
   module->invokeVisitor(&emitVisitor);
 

+ 0 - 4
tools/clang/lib/SPIRV/SpirvEmitter.cpp

@@ -1308,10 +1308,6 @@ void SpirvEmitter::doVarDecl(const VarDecl *decl) {
       needsLegalization = true;
   }
 
-  if (isRelaxedPrecisionType(decl->getType(), spirvOptions)) {
-    spvBuilder.decorateRelaxedPrecision(var);
-  }
-
   // All variables that are of opaque struct types should request legalization.
   if (!needsLegalization && isOpaqueStructType(decl->getType()))
     needsLegalization = true;

+ 3 - 2
tools/clang/lib/SPIRV/SpirvFunction.cpp

@@ -19,8 +19,9 @@ SpirvFunction::SpirvFunction(QualType returnType, SpirvType *functionType,
                              spv::FunctionControlMask control,
                              SourceLocation loc, llvm::StringRef name)
     : functionId(0), astReturnType(returnType), returnType(nullptr),
-      fnType(functionType), containsAlias(false), rvalue(false),
-      functionControl(control), functionLoc(loc), functionName(name) {}
+      fnType(functionType), relaxedPrecision(false), containsAlias(false),
+      rvalue(false), functionControl(control), functionLoc(loc),
+      functionName(name) {}
 
 bool SpirvFunction::invokeVisitor(Visitor *visitor, bool reverseOrder) {
   if (!visitor->visit(this, Visitor::Phase::Init))

+ 3 - 1
tools/clang/lib/SPIRV/SpirvType.cpp

@@ -213,7 +213,9 @@ operator==(const StructType::FieldInfo &that) const {
           matrixStride.getValue() == that.matrixStride.getValue()) &&
          // Either not have row major value, or have the same value
          (!isRowMajor.hasValue() ||
-          isRowMajor.getValue() == that.isRowMajor.getValue());
+          isRowMajor.getValue() == that.isRowMajor.getValue()) &&
+         // Both should have the same precision
+         isRelaxedPrecision == that.isRelaxedPrecision;
 }
 
 bool StructType::operator==(const StructType &that) const {

+ 48 - 0
tools/clang/test/CodeGenSPIRV/decoration.relaxed-precision.basic.hlsl

@@ -0,0 +1,48 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK:                      OpDecorate %in_var_TEXCOORD0 RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate %out_var_SV_Target RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate %param_var_x RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[coordValue:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[mainResult:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate %src_main RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate %x RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate %y RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[xValue1:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[comparison:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[xValue2:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[xValue3:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[xMulx:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[xValue4:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[yValue1:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[yValue2:%\d+]] RelaxedPrecision
+// CHECK-NEXT:                 OpDecorate [[yMuly:%\d+]] RelaxedPrecision
+
+// CHECK:  %in_var_TEXCOORD0 = OpVariable %_ptr_Input_float Input
+// CHECK: %out_var_SV_Target = OpVariable %_ptr_Output_float Output
+// CHECK:       %param_var_x = OpVariable %_ptr_Function_float Function
+
+// CHECK:     [[coordValue]] = OpLoad %float %in_var_TEXCOORD0
+// CHECK:     [[mainResult]] = OpFunctionCall %float %src_main %param_var_x
+min16float main(min16float x : TEXCOORD0) : SV_Target {
+// CHECK:          %src_main = OpFunction %float None {{%\d+}}
+// CHECK:                 %x = OpFunctionParameter %_ptr_Function_float
+// CHECK:                 %y = OpVariable %_ptr_Function_float Function
+// CHECK:        [[xValue1]] = OpLoad %float %x
+// CHECK:     [[comparison]] = OpFOrdGreaterThan %bool [[xValue1]] %float_0
+    min16float y;
+    if (x > 0) {
+// CHECK:        [[xValue2]] = OpLoad %float %x
+// CHECK:        [[xValue3]] = OpLoad %float %x
+// CHECK:          [[xMulx]] = OpFMul %float [[xValue2]] [[xValue3]]
+        y = x * x;
+    }
+    else {
+// CEHCK:        [[xValue4]] = OpLoad %float %x
+        y = x;
+    }
+// CHECK:        [[yValue1]] = OpLoad %float %y
+// CHECK:        [[yValue2]] = OpLoad %float %y
+// CHECK:          [[yMuly]] = OpFMul %float [[yValue1]] [[yValue2]]
+    return y * y;
+}

+ 43 - 0
tools/clang/test/CodeGenSPIRV/decoration.relaxed-precision.image.hlsl

@@ -0,0 +1,43 @@
+// Run: %dxc -T ps_6_0 -E main
+
+SamplerState           gSampler        : register(s4);
+SamplerComparisonState gCompareSampler : register(s5);
+
+// CHECK:               OpDecorate %t2f4 RelaxedPrecision
+// CHECK-NEXT:          OpDecorate %t2i4 RelaxedPrecision
+// CHECK-NEXT:          OpDecorate %t2f RelaxedPrecision
+// CHECK-NEXT:          OpDecorate [[t2f4_val:%\d+]] RelaxedPrecision
+// CHECK-NEXT:          OpDecorate [[image_op_1:%\d+]] RelaxedPrecision
+// CHECK-NEXT:          OpDecorate [[t2i4_val:%\d+]] RelaxedPrecision
+// CHECK-NEXT:          OpDecorate [[image_op_2:%\d+]] RelaxedPrecision
+// CHECK:               OpDecorate [[t2f_val:%\d+]] RelaxedPrecision
+// CHECK:               OpDecorate [[imgSampleExplicit:%\d+]] RelaxedPrecision
+// CHECK:               OpDecorate [[t2f_val_again:%\d+]] RelaxedPrecision
+// CHECK:               OpDecorate [[imgSampleImplicit:%\d+]] RelaxedPrecision
+
+// CHECK:          %t2f4 = OpVariable %_ptr_UniformConstant_type_2d_image_array UniformConstant
+// CHECK:          %t2i4 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+Texture2DArray<min16float4> t2f4   : register(t1);
+Texture2D<min12int4>        t2i4   : register(t2);
+Texture2D<min16float>       t2f    : register(t3);
+
+float4 main(float3 location: A) : SV_Target {
+// CHECK:   [[t2f4_val]] = OpLoad %type_2d_image_array %t2f4
+// CHECK: [[image_op_1]] = OpImageGather %v4float
+  float4 a = t2f4.GatherBlue(gSampler, location, int2(1, 2));
+
+// CHECK:   [[t2i4_val]] = OpLoad %type_2d_image %t2i4
+// CHECK: [[image_op_2]] = OpImageFetch %v4int
+  int4 b = t2i4.Load(location, int2(1, 2));
+
+// CHECK:           [[t2f_val]] = OpLoad %type_2d_image_0 %t2f
+// CHECK: [[imgSampleExplicit]] = OpImageSampleDrefExplicitLod
+  t2f.SampleCmpLevelZero(gCompareSampler, float2(0,0), 0);
+
+// CHECK:     [[t2f_val_again]] = OpLoad %type_2d_image_0 %t2f
+// CHECK: [[imgSampleImplicit]] = OpImageSampleImplicitLod
+  t2f.Sample(gSampler, float2(0,0));
+
+  return 1.0;
+}
+

+ 82 - 0
tools/clang/test/CodeGenSPIRV/decoration.relaxed-precision.struct.hlsl

@@ -0,0 +1,82 @@
+// Run: %dxc -T ps_6_0 -E main
+
+min16uint foo(min12int param);
+
+// CHECK:              OpMemberDecorate %S 1 RelaxedPrecision
+// CHECK-NEXT:         OpMemberDecorate %S 2 RelaxedPrecision
+// CHECK-NEXT:         OpMemberDecorate %S 3 RelaxedPrecision
+// CHECK-NEXT:         OpMemberDecorate %S 4 RelaxedPrecision
+struct S {
+  int a;
+  min12int b;
+  min16int c;
+  min16float d;
+  min10float e;
+  float f;
+};
+
+void main() {
+// CHECK-NEXT:         OpDecorate %h RelaxedPrecision
+// CHECK-NEXT:         OpDecorate %i RelaxedPrecision
+// CHECK-NEXT:         OpDecorate %j RelaxedPrecision
+// CHECK-NEXT:         OpDecorate %param_var_param RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[sb:%\d+]] RelaxedPrecision
+// Note: Decoration is missing for sa_plus_sb.
+// CHECK-NEXT:         OpDecorate [[sb2:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[sb2_int:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[sc:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[sb_plus_sc:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[sd:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[se:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[sd_plus_se:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[sb3:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[foo_result:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate %foo RelaxedPrecision
+// CHECK-NEXT:         OpDecorate %param RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[param_value:%\d+]] RelaxedPrecision
+// CHECK-NEXT:         OpDecorate [[param_plus_1:%\d+]] RelaxedPrecision
+
+// CHECK:         %S = OpTypeStruct %int %int %int %float %float %float
+  S s;
+// src_main function:
+// CHECK:              %h = OpVariable %_ptr_Function_int Function
+// CHECK:              %i = OpVariable %_ptr_Function_float Function
+// CHECK:              %j = OpVariable %_ptr_Function_uint Function
+// CHECK: param_var_param = OpVariable %_ptr_Function_int Function
+
+// CHECK:      [[s1ptr:%\d+]] = OpAccessChain %_ptr_Function_int %s %int_1
+// CHECK:              [[sb]] = OpLoad %int [[s1ptr]]
+// CHECK: [[sa_plus_sb:%\d+]] = OpIAdd %int
+// While s.b is RelaxedPrecision (min12int), s.a is not (int).
+// We can only annotate the OpIAdd as RelaxedPrecision if both were.
+  int g = s.a + s.b;
+
+// CHECK:  [[s1ptr:%\d+]] = OpAccessChain %_ptr_Function_int %s %int_1
+// CHECK:         [[sb2]] = OpLoad %int [[s1ptr]]
+// CHECK:     [[sb2_int]] = OpBitcast %int [[sb2]]
+// CHECK:  [[s2ptr:%\d+]] = OpAccessChain %_ptr_Function_int %s %int_2
+// CHECK:          [[sc]] = OpLoad %int [[s2ptr]]
+// CHECK:  [[sb_plus_sc]] = OpIAdd %int [[sb2_int]] [[sc]]
+  min16int h = s.b + s.c;
+
+// CHECK:  [[s3ptr:%\d+]] = OpAccessChain %_ptr_Function_float %s %int_3
+// CHECK:          [[sd]] = OpLoad %float [[s3ptr]]
+// CHECK:  [[s4ptr:%\d+]] = OpAccessChain %_ptr_Function_float %s %int_4
+// CHECK:          [[se]] = OpLoad %float [[s4ptr]]
+// CHECK:  [[sd_plus_se]] = OpFAdd %float [[sd]] [[se]]
+  min16float i = s.d + s.e;
+
+// CHECK:  [[s1ptr:%\d+]] = OpAccessChain %_ptr_Function_int %s %int_1
+// CHECK:         [[sb3]] = OpLoad %int [[s1ptr]]
+// CHECK:  [[foo_result]] = OpFunctionCall %uint %foo %param_var_param
+  min16uint j = foo(s.b);
+}
+
+// CHECK:            %foo = OpFunction %uint None %48
+// CHECK:          %param = OpFunctionParameter %_ptr_Function_int
+min16uint foo(min12int param) {
+// CHECK: [[param_value]] = OpLoad %int %param
+// CHECK:[[param_plus_1]] = OpIAdd %int
+  return param + 1;
+}
+

+ 10 - 0
tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp

@@ -1830,4 +1830,14 @@ TEST_F(FileTest, RayTracingNVLibrary) {
   runFileTest("raytracing.nv.library.hlsl");
 }
 
+TEST_F(FileTest, DecorationRelaxedPrecisionBasic) {
+  runFileTest("decoration.relaxed-precision.basic.hlsl");
+}
+TEST_F(FileTest, DecorationRelaxedPrecisionStruct) {
+  runFileTest("decoration.relaxed-precision.struct.hlsl");
+}
+TEST_F(FileTest, DecorationRelaxedPrecisionImage) {
+  runFileTest("decoration.relaxed-precision.image.hlsl");
+}
+
 } // namespace