Bläddra i källkod

Denorm to function attribute (#764)

- Denorm mode to function attribute not function annotation
- Adding validation rule for fp32-denorm-mode



* Fix from comments
Young Kim 7 år sedan
förälder
incheckning
8c55bbbe6d

+ 3 - 24
docs/DXIL.rst

@@ -130,7 +130,8 @@ Main two features that were introduced for DXIL1.1 (Shader Model 6.1) are view i
 
 DXIL 1.2 Changes
 ----------------
-* New format for type-annotations_ for functions to indicate floating point operations behavior for per function basis.
+* RawBufferLoad and RawBufferStore DXIL operations for ByteAddressBuffer and StructuredBuffer
+* Denorm mode as a function attribute for float32 "fp32-denorm-mode"=<value>
 
 LLVM Bitcode version
 --------------------
@@ -447,7 +448,6 @@ Idx Type
 === =====================================================================
 0    Structure Annotation
 1    Function Annotation
-2    Function Annotation2 (Not available before DXIL 1.2)
 === =====================================================================
 
 The second value represents the name, the third is a corresponding type metadata node.
@@ -489,27 +489,6 @@ Each **Parameter Annotation** contains Input/Output type, field annotation, and
   !14 = !{i32 1, !15, !13}
   !15 = !{i32 4, !"SV_Target", i32 7, i32 9}
 
-**DXIL 1.2 Change**
-Prior to DXIL 1.2, function annotations metadata only contained a list of parameter annotations, starting with the input parameter
-For DXIL 1.2, **function annotation** will contain FunctionFPFlag, followed by parameter annotations::
-
-  !7 = !{i32 2, void (float, float*)* @"main", !8}
-  !8 = !{!9, !10}
-  !9 = !{i32 0}
-  !10 = !{!11, !13, !15}
-  
-FunctionFPFlag is a flag to control the behavior of the floating point operation::
-
-  !9 = !{i32 0}
-
-Currently three values are valid for floating point flag
-
-* 0: FP32 math operations on denorm may or may not flush to zero
-* 1: FP32 math operations perserve Denorms
-* 2: FP32 math operations flush denormal output numbers to zero
-
-For operations on FP16/FP64 denormal numbers will preserve denormal numbers.
-
 Shader Properties and Capabilities
 ==================================
 
@@ -2788,6 +2767,7 @@ CONTAINER.PARTREPEATED                 DXIL Container must have only one of each
 CONTAINER.ROOTSIGNATUREINCOMPATIBLE    Root Signature in DXIL Container must be compatible with shader
 DECL.DXILFNEXTERN                      External function must be a DXIL function
 DECL.DXILNSRESERVED                    The DXIL reserved prefixes must only be used by built-in functions and types
+DECL.FNATTRIBUTE                       Functions should only contain known function attributes
 DECL.FNFLATTENPARAM                    Function parameters must not use struct types
 DECL.FNISCALLED                        Functions can only be used by call instructions
 DECL.NOTUSEDEXTERNAL                   External declaration should not be used
@@ -2877,7 +2857,6 @@ META.DUPLICATESYSVALUE                 System value may only appear once in sign
 META.ENTRYFUNCTION                     entrypoint not found
 META.FLAGSUSAGE                        Flags must match usage
 META.FORCECASEONSWITCH                 Attribute forcecase only works for switch
-META.FPFLAG                            Invalid funciton floating point flag.
 META.FUNCTIONANNOTATION                Cannot find function annotation for %0
 META.GLCNOTONAPPENDCONSUME             globallycoherent cannot be used with append/consume buffers
 META.INTEGERINTERPMODE                 Interpolation mode on integer must be Constant

+ 10 - 3
include/dxc/HLSL/DxilConstants.h

@@ -218,10 +218,10 @@ namespace DXIL {
   };
   // PackingKind-ENUM:END
 
-  /* <py::lines('FPDenormMode-ENUM')>hctdb_instrhelp.get_enum_decl("FPDenormMode", hide_val=False, sort_val=False)</py>*/
+  /* <py::lines('FPDenormMode-ENUM')>hctdb_instrhelp.get_enum_decl("Float32DenormMode", hide_val=False, sort_val=False)</py>*/
   // FPDenormMode-ENUM:BEGIN
-  // Floating point behavior
-  enum class FPDenormMode : unsigned {
+  // float32 denorm behavior
+  enum class Float32DenormMode : unsigned {
     Any = 0, // Undefined behavior for denormal numbers
     Preserve = 1, // Preserve both input and output
     FTZ = 2, // Preserve denormal inputs. Flush denorm outputs
@@ -941,6 +941,13 @@ namespace DXIL {
   // New data layout with native low precision types
   static const char* kNewLayoutString = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64";
 
+  // Function Attributes
+  // TODO: consider generating attributes from hctdb
+  static const char* kFP32DenormKindString          = "fp32-denorm-mode";
+  static const char* kFP32DenormValueAnyString      = "any";
+  static const char* kFP32DenormValuePreserveString = "preserve";
+  static const char* kFP32DenormValueFtzString      = "ftz";
+
 } // namespace DXIL
 
 } // namespace hlsl

+ 1 - 7
include/dxc/HLSL/DxilMetadataHelper.h

@@ -47,7 +47,6 @@ class DxilStructAnnotation;
 class DxilFieldAnnotation;
 class DxilFunctionAnnotation;
 class DxilParameterAnnotation;
-class DxilFunctionFPFlag;
 class RootSignatureHandle;
 class DxilViewIdState;
 struct DxilFunctionProps;
@@ -171,8 +170,7 @@ public:
   static const char kDxilTypeSystemMDName[];
   static const char kDxilTypeSystemHelperVariablePrefix[];
   static const unsigned kDxilTypeSystemStructTag                  = 0;
-  static const unsigned kDxilTypeSystemFunctionTag                = 1; // For DXIL <= 1.1
-  static const unsigned kDxilTypeSystemFunction2Tag               = 2; // For DXIL >= 1.2
+  static const unsigned kDxilTypeSystemFunctionTag                = 1;
   static const unsigned kDxilFieldAnnotationSNormTag              = 0;
   static const unsigned kDxilFieldAnnotationUNormTag              = 1;
   static const unsigned kDxilFieldAnnotationMatrixTag             = 2;
@@ -335,14 +333,10 @@ public:
   void LoadDxilFieldAnnotation(const llvm::MDOperand &MDO, DxilFieldAnnotation &FA);
   llvm::Metadata *EmitDxilFunctionAnnotation(const DxilFunctionAnnotation &FA);
   void LoadDxilFunctionAnnotation(const llvm::MDOperand &MDO, DxilFunctionAnnotation &FA);
-  llvm::Metadata *EmitDxilFunctionAnnotation2(const DxilFunctionAnnotation &FA);
-  void LoadDxilFunctionAnnotation2(const llvm::MDOperand &MDO, DxilFunctionAnnotation &FA);
   llvm::Metadata *EmitDxilParamAnnotation(const DxilParameterAnnotation &PA);
   void LoadDxilParamAnnotation(const llvm::MDOperand &MDO, DxilParameterAnnotation &PA);
   llvm::Metadata *EmitDxilParamAnnotations(const DxilFunctionAnnotation &FA);
   void LoadDxilParamAnnotations(const llvm::MDOperand &MDO, DxilFunctionAnnotation &FA);
-  llvm::Metadata *EmitDxilFunctionFPFlag(const DxilFunctionFPFlag &flag);
-  void LoadDxilFunctionFPFlag(const llvm::MDOperand &MDO, DxilFunctionAnnotation &FA);
 
   // Function props.
   llvm::MDTuple *EmitDxilFunctionProps(const hlsl::DxilFunctionProps *props,

+ 0 - 23
include/dxc/HLSL/DxilTypeSystem.h

@@ -138,25 +138,6 @@ private:
   std::vector<unsigned> m_semanticIndex;
 };
 
-/// Use this class to represent floating point operations flags for LLVM function
-class DxilFunctionFPFlag {
-  friend class DxilFunctionAnnotation;
-public:
-  static const unsigned kFPDenormMask      = 0x00000007;
-  static const unsigned kFPDenormOffset    = 0;
-
-  void SetFP32DenormMode(const DXIL::FPDenormMode mode);
-  DXIL::FPDenormMode GetFP32DenormMode();
-
-  uint32_t GetFlagValue();
-  const uint32_t GetFlagValue() const;
-  void SetFlagValue(const uint32_t flag);
-
-  DxilFunctionFPFlag(uint32_t flag = 0) : m_flag(flag) {}
-private:
-  uint32_t m_flag;
-};
-
 /// Use this class to represent LLVM function annotation.
 class DxilFunctionAnnotation {
   friend class DxilTypeSystem;
@@ -168,13 +149,10 @@ public:
   const llvm::Function *GetFunction() const;
   DxilParameterAnnotation &GetRetTypeAnnotation();
   const DxilParameterAnnotation &GetRetTypeAnnotation() const;
-  DxilFunctionFPFlag &GetFlag();
-  const DxilFunctionFPFlag &GetFlag() const;
 private:
   const llvm::Function *m_pFunction;
   std::vector<DxilParameterAnnotation> m_parameterAnnotations;
   DxilParameterAnnotation m_retTypeAnnotation;
-  DxilFunctionFPFlag m_fpFlag;
 };
 
 /// Use this class to represent structure type annotations in HL and DXIL.
@@ -193,7 +171,6 @@ public:
   StructAnnotationMap &GetStructAnnotationMap();
 
   DxilFunctionAnnotation *AddFunctionAnnotation(const llvm::Function *pFunction);
-  DxilFunctionAnnotation *AddFunctionAnnotationWithFPFlag(const llvm::Function *pFunction, const DxilFunctionFPFlag *flag);
   DxilFunctionAnnotation *GetFunctionAnnotation(const llvm::Function *pFunction);
   const DxilFunctionAnnotation *GetFunctionAnnotation(const llvm::Function *pFunction) const;
   void EraseFunctionAnnotation(const llvm::Function *pFunction);

+ 1 - 1
include/dxc/HLSL/DxilValidation.h

@@ -42,6 +42,7 @@ enum class ValidationRule : unsigned {
   // Declaration
   DeclDxilFnExtern, // External function must be a DXIL function
   DeclDxilNsReserved, // The DXIL reserved prefixes must only be used by built-in functions and types
+  DeclFnAttribute, // Functions should only contain known function attributes
   DeclFnFlattenParam, // Function parameters must not use struct types
   DeclFnIsCalled, // Functions can only be used by call instructions
   DeclNotUsedExternal, // External declaration should not be used
@@ -129,7 +130,6 @@ enum class ValidationRule : unsigned {
   MetaDenseResIDs, // Resource identifiers must be zero-based and dense
   MetaDuplicateSysValue, // System value may only appear once in signature
   MetaEntryFunction, // entrypoint not found
-  MetaFPFlag, // Invalid funciton floating point flag.
   MetaFlagsUsage, // Flags must match usage
   MetaForceCaseOnSwitch, // Attribute forcecase only works for switch
   MetaFunctionAnnotation, // Cannot find function annotation for %0

+ 3 - 4
include/dxc/HLSL/HLModule.h

@@ -130,7 +130,6 @@ public:
 
   DxilFunctionAnnotation *GetFunctionAnnotation(llvm::Function *F);
   DxilFunctionAnnotation *AddFunctionAnnotation(llvm::Function *F);
-  DxilFunctionAnnotation *AddFunctionAnnotationWithFPDenormMode(llvm::Function *F, DXIL::FPDenormMode mode);
 
   void AddResourceTypeAnnotation(llvm::Type *Ty, DXIL::ResourceClass resClass,
                                  DXIL::ResourceKind kind);
@@ -138,8 +137,8 @@ public:
   DXIL::ResourceKind  GetResourceKind(llvm::Type *Ty);
 
   // Float Denorm mode.
-  void SetFPDenormMode(const DXIL::FPDenormMode mode);
-  DXIL::FPDenormMode GetFPDenormMode() const;
+  void SetFloat32DenormMode(const DXIL::Float32DenormMode mode);
+  DXIL::Float32DenormMode GetFloat32DenormMode() const;
 
   // HLDXIR metadata manipulation.
   /// Serialize HLDXIR in-memory form to metadata form.
@@ -255,7 +254,7 @@ private:
   unsigned m_DxilMinor;
   unsigned m_ValMajor;
   unsigned m_ValMinor;
-  DXIL::FPDenormMode m_FPDenormMode;
+  DXIL::Float32DenormMode m_Float32DenormMode;
   HLOptions m_Options;
   std::unique_ptr<OP> m_pOP;
   size_t m_pUnused;

+ 1 - 1
include/dxc/Support/HLSLOptions.h

@@ -108,7 +108,7 @@ public:
   llvm::StringRef RootSignatureSource; // OPT_setrootsignature
   llvm::StringRef VerifyRootSignatureSource; //OPT_verifyrootsignature
   llvm::StringRef RootSignatureDefine; // OPT_rootsig_define
-  llvm::StringRef FPDenormalMode; // OPT_fdenormal-fp-math
+  llvm::StringRef FloatDenormalMode; // OPT_denorm
 
   bool AllResourcesBound; // OPT_all_resources_bound
   bool AstDump; // OPT_ast_dump

+ 8 - 8
lib/DxcSupport/HLSLOptions.cpp

@@ -303,21 +303,21 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
 
   opts.IgnoreLineDirectives = Args.hasFlag(OPT_ignore_line_directives, OPT_INVALID, false);
 
-  opts.FPDenormalMode = Args.getLastArgValue(OPT_denorm);
+  opts.FloatDenormalMode = Args.getLastArgValue(OPT_denorm);
   // Check if a given denormalized value is valid
-  if (!opts.FPDenormalMode.empty()) {
-    if (!(opts.FPDenormalMode.equals_lower("preserve") ||
-          opts.FPDenormalMode.equals_lower("ftz") ||
-          opts.FPDenormalMode.equals_lower("any"))) {
-      errors << "Unsupported value '" << opts.FPDenormalMode
+  if (!opts.FloatDenormalMode.empty()) {
+    if (!(opts.FloatDenormalMode.equals_lower("preserve") ||
+          opts.FloatDenormalMode.equals_lower("ftz") ||
+          opts.FloatDenormalMode.equals_lower("any"))) {
+      errors << "Unsupported value '" << opts.FloatDenormalMode
           << "' for denorm option.";
       return 1;
     }
   }
 
-  // Check options only allowed in shader model >= 6.2
+  // Check options only allowed in shader model >= 6.2FPDenormalMode
   if (opts.TargetProfile.empty() || !opts.TargetProfile.endswith_lower("6_2")) {
-    if (!opts.FPDenormalMode.empty()) {
+    if (!opts.FloatDenormalMode.empty()) {
       errors << "denorm option is only allowed for shader model 6.2 and above.";
       return 1;
     }

+ 3 - 54
lib/HLSL/DxilMetadataHelper.cpp

@@ -696,27 +696,14 @@ void DxilMDHelper::EmitDxilTypeSystem(DxilTypeSystem &TypeSystem, vector<GlobalV
 
   auto &FuncMap = TypeSystem.GetFunctionAnnotationMap();
   vector<Metadata *> MDFuncVals;
-  unsigned major, minor;
-  LoadDxilVersion(major, minor);
-  if (major == 1 && minor <= 1) {
-    MDFuncVals.emplace_back(Uint32ToConstMD(kDxilTypeSystemFunctionTag)); // Tag
-  }
-  else {
-    DXASSERT(major == 1 && minor >= 2, "Invalid DXIL Version.");
-    MDFuncVals.emplace_back(Uint32ToConstMD(kDxilTypeSystemFunction2Tag));
-  }
+  MDFuncVals.emplace_back(Uint32ToConstMD(kDxilTypeSystemFunctionTag)); // Tag
   for (auto it = FuncMap.begin(); it != FuncMap.end(); ++it) {
     DxilFunctionAnnotation *pA = it->second.get();
     MDFuncVals.push_back(ValueAsMetadata::get(const_cast<Function*>(pA->GetFunction())));
     // Emit function annotations.
 
    Metadata *pMD;
-    if (major == 1 && minor <= 1) {
-      pMD = EmitDxilFunctionAnnotation(*pA);
-    }
-    else {
-      pMD = EmitDxilFunctionAnnotation2(*pA);
-    }
+    pMD = EmitDxilFunctionAnnotation(*pA);
     MDFuncVals.push_back(pMD);
   }
 
@@ -757,20 +744,13 @@ void DxilMDHelper::LoadDxilTypeSystemNode(const llvm::MDTuple &MDT,
       DxilStructAnnotation *pSA = TypeSystem.AddStructAnnotation(pGVType);
       LoadDxilStructAnnotation(MDT.getOperand(i + 1), *pSA);
     }
-  } else if (Tag == kDxilTypeSystemFunctionTag) {
+  } else {
     IFTBOOL((MDT.getNumOperands() & 0x1) == 1, DXC_E_INCORRECT_DXIL_METADATA);
     for (unsigned i = 1; i < MDT.getNumOperands(); i += 2) {
       Function *F = dyn_cast<Function>(ValueMDToValue(MDT.getOperand(i)));
       DxilFunctionAnnotation *pFA = TypeSystem.AddFunctionAnnotation(F);
       LoadDxilFunctionAnnotation(MDT.getOperand(i + 1), *pFA);
     }
-  } else {
-    IFTBOOL(Tag == kDxilTypeSystemFunction2Tag, DXC_E_INCORRECT_DXIL_METADATA);
-    for (unsigned i = 1; i < MDT.getNumOperands(); i += 2) {
-      Function *F = dyn_cast<Function>(ValueMDToValue(MDT.getOperand(i)));
-      DxilFunctionAnnotation *pFA = TypeSystem.AddFunctionAnnotation(F);
-      LoadDxilFunctionAnnotation2(MDT.getOperand(i + 1), *pFA);
-    }
   }
 }
 
@@ -820,7 +800,6 @@ void DxilMDHelper::LoadDxilStructAnnotation(const MDOperand &MDO, DxilStructAnno
   }
 }
 
-// For <= 1.1: Function Annotation is a tuple of Parameter Annotataions, starting with the return type.
 Metadata *
 DxilMDHelper::EmitDxilFunctionAnnotation(const DxilFunctionAnnotation &FA) {
   return EmitDxilParamAnnotations(FA);
@@ -831,23 +810,6 @@ void DxilMDHelper::LoadDxilFunctionAnnotation(const MDOperand &MDO,
   LoadDxilParamAnnotations(MDO, FA);
 }
 
-// For >= 1.2: Function Annotation is a tuple of 1) FunctionFPFlag and 2) Parameter Annotations
-llvm::Metadata *DxilMDHelper::EmitDxilFunctionAnnotation2(const DxilFunctionAnnotation &FA) {
-  vector<Metadata *> MDVals(2);
-  MDVals[0] = EmitDxilFunctionFPFlag(FA.GetFlag());
-  MDVals[1] = EmitDxilParamAnnotations(FA);
-  return MDNode::get(m_Ctx, MDVals);
-}
-
-void DxilMDHelper::LoadDxilFunctionAnnotation2(const llvm::MDOperand &MDO, DxilFunctionAnnotation &FA) {
-  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
-  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
-  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
-  IFTBOOL(pTupleMD->getNumOperands() == 2, DXC_E_INCORRECT_DXIL_METADATA);
-  LoadDxilFunctionFPFlag(pTupleMD->getOperand(0), FA);
-  LoadDxilParamAnnotations(pTupleMD->getOperand(1), FA);
-}
-
 llvm::Metadata *
 DxilMDHelper::EmitDxilParamAnnotations(const DxilFunctionAnnotation &FA) {
   vector<Metadata *> MDParamAnnotations(FA.GetNumParameters() + 1);
@@ -874,19 +836,6 @@ void DxilMDHelper::LoadDxilParamAnnotations(const llvm::MDOperand &MDO,
   }
 }
 
-Metadata *DxilMDHelper::EmitDxilFunctionFPFlag(const DxilFunctionFPFlag &flag) {
-  return MDNode::get(m_Ctx, Int32ToConstMD(flag.GetFlagValue()));
-}
-
-void DxilMDHelper::LoadDxilFunctionFPFlag(const llvm::MDOperand &MDO,
-                                          DxilFunctionAnnotation &FA) {
-  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
-  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
-  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
-  IFTBOOL(pTupleMD->getNumOperands() == 1, DXC_E_INCORRECT_DXIL_METADATA);
-  FA.GetFlag().SetFlagValue(ConstMDToUint32(pTupleMD->getOperand(0)));
-}
-
 Metadata *
 DxilMDHelper::EmitDxilParamAnnotation(const DxilParameterAnnotation &PA) {
   vector<Metadata *> MDVals(3);

+ 9 - 4
lib/HLSL/DxilPreparePasses.cpp

@@ -152,6 +152,14 @@ Function *StripFunctionParameter(Function *F, DxilModule &DM,
   // Splice the body of the old function right into the new function.
   NewFunc->getBasicBlockList().splice(NewFunc->begin(), F->getBasicBlockList());
 
+  // Keep necessary function attributes
+  AttributeSet attributeSet = F->getAttributes();
+  if (attributeSet.hasAttribute(AttributeSet::FunctionIndex, DXIL::kFP32DenormKindString)) {
+    Attribute attribute = attributeSet.getAttribute(AttributeSet::FunctionIndex, DXIL::kFP32DenormKindString);
+    DXASSERT(attribute.isStringAttribute(), "otherwise we have wrong fp-denorm-mode attribute.");
+    NewFunc->addFnAttr(attribute.getKindAsString(), attribute.getValueAsString());
+  }
+
   // Patch the pointer to LLVM function in debug info descriptor.
   auto DI = FunctionDIs.find(F);
   if (DI != FunctionDIs.end()) {
@@ -167,12 +175,9 @@ Function *StripFunctionParameter(Function *F, DxilModule &DM,
     DM.ReplaceDxilEntrySignature(F, NewFunc);
     DM.ReplaceDxilFunctionProps(F, NewFunc);
   }
-  // Save function fp flag
-  DxilFunctionFPFlag flag;
-  flag.SetFlagValue(DM.GetTypeSystem().GetFunctionAnnotation(F)->GetFlag().GetFlagValue());
   DM.GetTypeSystem().EraseFunctionAnnotation(F);
   F->eraseFromParent();
-  DM.GetTypeSystem().AddFunctionAnnotationWithFPFlag(NewFunc, &flag);
+  DM.GetTypeSystem().AddFunctionAnnotation(NewFunc);
   return NewFunc;
 }
 

+ 1 - 42
lib/HLSL/DxilTypeSystem.cpp

@@ -158,39 +158,6 @@ const Function *DxilFunctionAnnotation::GetFunction() const {
   return m_pFunction;
 }
 
-DxilFunctionFPFlag &DxilFunctionAnnotation::GetFlag() {
-  return m_fpFlag;
-}
-
-const DxilFunctionFPFlag &DxilFunctionAnnotation::GetFlag() const {
-  return m_fpFlag;
-}
-
-//------------------------------------------------------------------------------
-//
-// DxilFunctionFPFlag class methods.
-//
-
-void DxilFunctionFPFlag::SetFP32DenormMode(const DXIL::FPDenormMode mode) {
-  m_flag |= ((uint32_t)mode & kFPDenormMask) << kFPDenormOffset;
-}
-
-DXIL::FPDenormMode DxilFunctionFPFlag::GetFP32DenormMode() {
-  return (DXIL::FPDenormMode)((m_flag >> kFPDenormOffset) & kFPDenormMask);
-}
-
-uint32_t DxilFunctionFPFlag::GetFlagValue() {
-  return m_flag;
-}
-
-const uint32_t DxilFunctionFPFlag::GetFlagValue() const {
-  return m_flag;
-}
-
-void DxilFunctionFPFlag::SetFlagValue(const uint32_t flag) {
-  m_flag = flag;
-}
-
 //------------------------------------------------------------------------------
 //
 // DxilStructAnnotationSystem class methods.
@@ -239,19 +206,11 @@ DxilTypeSystem::StructAnnotationMap &DxilTypeSystem::GetStructAnnotationMap() {
 }
 
 DxilFunctionAnnotation *DxilTypeSystem::AddFunctionAnnotation(const Function *pFunction) {
-  DxilFunctionFPFlag flag;
-  flag.SetFlagValue(0);
-  DxilFunctionAnnotation *pA = AddFunctionAnnotationWithFPFlag(pFunction, &flag);
-  return pA;
-}
-
-DxilFunctionAnnotation *DxilTypeSystem::AddFunctionAnnotationWithFPFlag(const Function *pFunction, const DxilFunctionFPFlag *pFlag) {
   DXASSERT_NOMSG(m_FunctionAnnotations.find(pFunction) == m_FunctionAnnotations.end());
   DxilFunctionAnnotation *pA = new DxilFunctionAnnotation();
   m_FunctionAnnotations[pFunction] = unique_ptr<DxilFunctionAnnotation>(pA);
   pA->m_pFunction = pFunction;
   pA->m_parameterAnnotations.resize(pFunction->getFunctionType()->getNumParams());
-  pA->GetFlag().SetFlagValue(pFlag->GetFlagValue());
   return pA;
 }
 
@@ -355,7 +314,7 @@ void DxilTypeSystem::CopyFunctionAnnotation(const llvm::Function *pDstFunction,
   if (GetFunctionAnnotation(pDstFunction))
     return;
 
-  DxilFunctionAnnotation *dstAnnot = AddFunctionAnnotationWithFPFlag(pDstFunction, &src.GetFunctionAnnotation(pSrcFunction)->GetFlag());
+  DxilFunctionAnnotation *dstAnnot = AddFunctionAnnotation(pDstFunction);
 
   // Copy the annotation.
   *dstAnnot = *annot;

+ 23 - 20
lib/HLSL/DxilValidation.cpp

@@ -109,7 +109,6 @@ const char *hlsl::GetValidationRuleText(ValidationRule value) {
     case hlsl::ValidationRule::MetaBarycentricsInterpolation: return "SV_Barycentrics cannot be used with 'nointerpolation' type";
     case hlsl::ValidationRule::MetaBarycentricsFloat3: return "only 'float3' type is allowed for SV_Barycentrics.";
     case hlsl::ValidationRule::MetaBarycentricsTwoPerspectives: return "There can only be up to two input attributes of SV_Barycentrics with different perspective interpolation mode.";
-    case hlsl::ValidationRule::MetaFPFlag: return "Invalid funciton floating point flag.";
     case hlsl::ValidationRule::InstrOload: return "DXIL intrinsic overload must be valid";
     case hlsl::ValidationRule::InstrCallOload: return "Call to DXIL intrinsic '%0' does not match an allowed overload signature";
     case hlsl::ValidationRule::InstrPtrBitCast: return "Pointer type bitcast must be have same size";
@@ -248,6 +247,7 @@ const char *hlsl::GetValidationRuleText(ValidationRule value) {
     case hlsl::ValidationRule::DeclUsedExternalFunction: return "External function '%0' is unused";
     case hlsl::ValidationRule::DeclFnIsCalled: return "Function '%0' is used for something other than calling";
     case hlsl::ValidationRule::DeclFnFlattenParam: return "Type '%0' is a struct type but is used as a parameter in function '%1'";
+    case hlsl::ValidationRule::DeclFnAttribute: return "Function '%0' contains invalid attribute '%1' with value '%2'";
   }
   // VALRULE-TEXT:END
   llvm_unreachable("invalid value");
@@ -553,6 +553,10 @@ struct ValidationContext {
     Ty->print(OSS);
     EmitFormatError(rule, { OSS.str() });
   }
+
+  void EmitFnAttributeError(Function *F, StringRef Kind, StringRef Value) {
+    EmitFormatError(ValidationRule::DeclFnAttribute, { F->getName(), Kind, Value });
+  }
 };
 
 static bool ValidateOpcodeInProfile(DXIL::OpCode opcode,
@@ -2360,6 +2364,21 @@ static void ValidateInstructionMetadata(Instruction *I,
   }
 }
 
+static void ValidateFunctionAttribute(Function *F, ValidationContext &ValCtx) {
+  AttributeSet attrSet = F->getAttributes();
+  // fp32-denorm-mode
+  if (attrSet.hasAttribute(AttributeSet::FunctionIndex, DXIL::kFP32DenormKindString)) {
+    Attribute attr = attrSet.getAttribute(AttributeSet::FunctionIndex, DXIL::kFP32DenormKindString);
+    StringRef value = attr.getValueAsString();
+    if (!value.equals(DXIL::kFP32DenormValueAnyString) &&
+      !value.equals(DXIL::kFP32DenormValueFtzString) &&
+      !value.equals(DXIL::kFP32DenormValuePreserveString))
+    {
+      ValCtx.EmitFnAttributeError(F, attr.getKindAsString(), attr.getValueAsString());
+    }
+  }
+}
+
 static void ValidateFunctionMetadata(Function *F, ValidationContext &ValCtx) {
   SmallVector<std::pair<unsigned, MDNode *>, 2> MDNodes;
   F->getAllMetadata(MDNodes);
@@ -2661,6 +2680,8 @@ static void ValidateFunction(Function &F, ValidationContext &ValCtx) {
     ValidateFunctionBody(&F, ValCtx);
   }
 
+  ValidateFunctionAttribute(&F, ValCtx);
+
   if (F.hasMetadata()) {
     ValidateFunctionMetadata(&F, ValCtx);
   }
@@ -2833,28 +2854,10 @@ static void ValidateTypeAnnotation(ValidationContext &ValCtx) {
       ConstantInt *tag = mdconst::extract<ConstantInt>(TANode->getOperand(0));
       uint64_t tagValue = tag->getZExtValue();
       if (tagValue != DxilMDHelper::kDxilTypeSystemStructTag &&
-          tagValue != DxilMDHelper::kDxilTypeSystemFunctionTag &&
-          tagValue != DxilMDHelper::kDxilTypeSystemFunction2Tag) {
+          tagValue != DxilMDHelper::kDxilTypeSystemFunctionTag) {
           ValCtx.EmitMetaError(TANode, ValidationRule::MetaWellFormed);
           return;
       }
-      if (tagValue == DxilMDHelper::kDxilTypeSystemFunction2Tag) {
-          for (unsigned j = 2, jEnd = TANode->getNumOperands();
-              j != jEnd; ++j) {
-            MDTuple *FA = dyn_cast<MDTuple>(TANode->getOperand(j));
-            if (FA == nullptr) {
-                ValCtx.EmitMetaError(FA, ValidationRule::MetaWellFormed);
-                return;
-            }
-            MDNode *FlagNode = dyn_cast<MDNode>(FA->getOperand(0));
-            uint64_t flagValue = mdconst::extract<ConstantInt>(FlagNode->getOperand(0))->getZExtValue();
-            if (flagValue != (uint64_t)DXIL::FPDenormMode::Any &&
-                flagValue != (uint64_t)DXIL::FPDenormMode::FTZ &&
-                flagValue != (uint64_t)DXIL::FPDenormMode::Preserve) {
-                ValCtx.EmitMetaError(FA->getOperand(0), ValidationRule::MetaFPFlag);
-            }
-          }
-      }
     }
   }
 }

+ 4 - 11
lib/HLSL/HLModule.cpp

@@ -359,13 +359,6 @@ DxilFunctionAnnotation *HLModule::AddFunctionAnnotation(llvm::Function *F) {
   return m_pTypeSystem->AddFunctionAnnotation(F);
 }
 
-DxilFunctionAnnotation *HLModule::AddFunctionAnnotationWithFPDenormMode(llvm::Function *F, DXIL::FPDenormMode mode) {
-  DXASSERT(m_pTypeSystem->GetFunctionAnnotation(F) == nullptr, "function annotataion already exist");
-  DxilFunctionFPFlag flag(0);
-  flag.SetFP32DenormMode(mode);
-  return m_pTypeSystem->AddFunctionAnnotationWithFPFlag(F, &flag);
-}
-
 void HLModule::AddResourceTypeAnnotation(llvm::Type *Ty,
                                          DXIL::ResourceClass resClass,
                                          DXIL::ResourceKind kind) {
@@ -400,12 +393,12 @@ static unsigned GetFloatAt(MDTuple *tuple, unsigned idx) {
   return DxilMDHelper::ConstMDToFloat(tuple->getOperand(idx));
 }
 
-DXIL::FPDenormMode HLModule::GetFPDenormMode() const {
-  return m_FPDenormMode;
+DXIL::Float32DenormMode HLModule::GetFloat32DenormMode() const {
+  return m_Float32DenormMode;
 }
 
-void HLModule::SetFPDenormMode(const DXIL::FPDenormMode mode) {
-  m_FPDenormMode = mode;
+void HLModule::SetFloat32DenormMode(const DXIL::Float32DenormMode mode) {
+  m_Float32DenormMode = mode;
 }
 
 static const StringRef kHLDxilFunctionPropertiesMDName           = "dx.fnprops";

+ 1 - 1
lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp

@@ -5888,7 +5888,7 @@ void SROA_Parameter_HLSL::createFlattenedFunction(Function *F) {
     funcDI->replaceFunction(flatF);
 
   // Create FunctionAnnotation for flatF.
-  DxilFunctionAnnotation *flatFuncAnnotation = m_pHLModule->AddFunctionAnnotationWithFPDenormMode(flatF, m_pHLModule->GetFPDenormMode());
+  DxilFunctionAnnotation *flatFuncAnnotation = m_pHLModule->AddFunctionAnnotation(flatF);
   
   // Don't need to set Ret Info, flatF always return void now.
 

+ 1 - 1
tools/clang/include/clang/Frontend/CodeGenOptions.h

@@ -199,7 +199,7 @@ public:
   /// Signature packing mode (0 == default for target)
   unsigned HLSLSignaturePackingStrategy = 0;
   /// denormalized number mode ("ieee" for default)
-  hlsl::DXIL::FPDenormMode HLSLFlushFPDenorm;
+  hlsl::DXIL::Float32DenormMode HLSLFloat32DenormMode;
   // HLSL Change Ends
   /// Regular expression to select optimizations for which we should enable
   /// optimization remarks. Transformation passes whose name matches this

+ 12 - 3
tools/clang/lib/CodeGen/CGHLSLMS.cpp

@@ -410,7 +410,7 @@ CGMSHLSLRuntime::CGMSHLSLRuntime(CodeGenModule &CGM)
   DXVERIFY_NOMSG(globalCBIndex == m_pHLModule->AddCBuffer(std::move(CB)));
 
   // set Float Denorm Mode
-  m_pHLModule->SetFPDenormMode(CGM.getCodeGenOpts().HLSLFlushFPDenorm);
+  m_pHLModule->SetFloat32DenormMode(CGM.getCodeGenOpts().HLSLFloat32DenormMode);
 
 }
 
@@ -1135,6 +1135,15 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
     return;
   }
 
+  if (m_pHLModule->GetFloat32DenormMode() == DXIL::Float32DenormMode::FTZ) {
+    F->addFnAttr(DXIL::kFP32DenormKindString, DXIL::kFP32DenormValueFtzString);
+  }
+  else if (m_pHLModule->GetFloat32DenormMode() == DXIL::Float32DenormMode::Preserve) {
+    F->addFnAttr(DXIL::kFP32DenormKindString, DXIL::kFP32DenormValuePreserveString);
+  }
+  else if (m_pHLModule->GetFloat32DenormMode() == DXIL::Float32DenormMode::Any) {
+    F->addFnAttr(DXIL::kFP32DenormKindString, DXIL::kFP32DenormValueAnyString);
+  }
   // Set entry function
   const std::string &entryName = m_pHLModule->GetEntryFunctionName();
   bool isEntry = FD->getNameAsString() == entryName;
@@ -1413,7 +1422,7 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
   }
 
   DxilFunctionAnnotation *FuncAnnotation =
-      m_pHLModule->AddFunctionAnnotationWithFPDenormMode(F, m_pHLModule->GetFPDenormMode());
+      m_pHLModule->AddFunctionAnnotation(F);
   bool bDefaultRowMajor = m_pHLModule->GetHLOptions().bDefaultRowMajor;
 
   // Param Info
@@ -3869,7 +3878,7 @@ static void CloneShaderEntry(Function *ShaderF, StringRef EntryName,
 
   // Copy function annotation.
   DxilFunctionAnnotation *shaderAnnot = HLM.GetFunctionAnnotation(ShaderF);
-  DxilFunctionAnnotation *annot = HLM.AddFunctionAnnotationWithFPDenormMode(F, HLM.GetFPDenormMode());
+  DxilFunctionAnnotation *annot = HLM.AddFunctionAnnotation(F);
 
   DxilParameterAnnotation &retAnnot = shaderAnnot->GetRetTypeAnnotation();
   DxilParameterAnnotation &cloneRetAnnot = annot->GetRetTypeAnnotation();

+ 7 - 0
tools/clang/test/CodeGenHLSL/functionAttribute.hlsl

@@ -0,0 +1,7 @@
+// RUN: %dxc -E main -T ps_6_2 -denorm ftz %s | FileCheck %s
+
+// CHECK: @main
+// CHECK: attributes #{{.*}} = { "fp32-denorm-mode"="ftz" }
+float4 main(float4 col : COL) : SV_Target {
+    return col;
+}

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

@@ -810,15 +810,18 @@ public:
     if (Opts.IEEEStrict)
       compiler.getCodeGenOpts().UnsafeFPMath = true;
 
-    if (Opts.FPDenormalMode.empty() || Opts.FPDenormalMode.equals_lower(StringRef("any"))) {
-      compiler.getCodeGenOpts().HLSLFlushFPDenorm = DXIL::FPDenormMode::Any;
+    if (Opts.FloatDenormalMode.empty()) {
+      compiler.getCodeGenOpts().HLSLFloat32DenormMode = DXIL::Float32DenormMode::Reserve7; // undefined
     }
-    else if (Opts.FPDenormalMode.equals_lower(StringRef("ftz"))) {
-      compiler.getCodeGenOpts().HLSLFlushFPDenorm = DXIL::FPDenormMode::FTZ;
+    else if (Opts.FloatDenormalMode.equals_lower(StringRef("any"))) {
+      compiler.getCodeGenOpts().HLSLFloat32DenormMode = DXIL::Float32DenormMode::Any;
+    }
+    else if (Opts.FloatDenormalMode.equals_lower(StringRef("ftz"))) {
+      compiler.getCodeGenOpts().HLSLFloat32DenormMode = DXIL::Float32DenormMode::FTZ;
     }
     else {
-      DXASSERT(Opts.FPDenormalMode.equals_lower(StringRef("preserve")), "else opts should have been rejected");
-      compiler.getCodeGenOpts().HLSLFlushFPDenorm = DXIL::FPDenormMode::Preserve;
+      DXASSERT(Opts.FloatDenormalMode.equals_lower(StringRef("preserve")), "else opts should have been rejected");
+      compiler.getCodeGenOpts().HLSLFloat32DenormMode = DXIL::Float32DenormMode::Preserve;
     }
 
     if (Opts.DisableOptimizations)

+ 6 - 0
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -555,6 +555,7 @@ public:
   TEST_METHOD(CodeGenFModPS)
   TEST_METHOD(CodeGenFuncCast)
   TEST_METHOD(CodeGenFunctionalCast)
+  TEST_METHOD(CodeGenFunctionAttribute)
   TEST_METHOD(CodeGenGather)
   TEST_METHOD(CodeGenGatherCmp)
   TEST_METHOD(CodeGenGatherCubeOffset)
@@ -3372,6 +3373,11 @@ TEST_F(CompilerTest, CodeGenFunctionalCast) {
   CodeGenTestCheck(L"..\\CodeGenHLSL\\functionalCast.hlsl");
 }
 
+TEST_F(CompilerTest, CodeGenFunctionAttribute) {
+  if (m_ver.SkipDxilVersion(1, 2)) return;
+  CodeGenTestCheck(L"..\\CodeGenHLSL\\functionAttribute.hlsl");
+}
+
 TEST_F(CompilerTest, CodeGenGather) {
   CodeGenTestCheck(L"..\\CodeGenHLSL\\gather.hlsl");
 }

+ 0 - 45
tools/clang/unittests/HLSL/DxilModuleTest.cpp

@@ -48,8 +48,6 @@ public:
   TEST_METHOD(LoadDxilModule_1_1);
   TEST_METHOD(LoadDxilModule_1_2);
 
-  TEST_METHOD(FunctionFPFlag);
-
   // Precise query tests.
   TEST_METHOD(Precise1);
   TEST_METHOD(Precise2);
@@ -218,49 +216,6 @@ TEST_F(DxilModuleTest, LoadDxilModule_1_2) {
   VERIFY_IS_TRUE(vMinor == 2);
 }
 
-TEST_F(DxilModuleTest, FunctionFPFlag) {
-  std::vector<std::pair<DXIL::FPDenormMode, LPCWSTR>> modeMap = {
-    {DXIL::FPDenormMode::Any, L"any"},
-    {DXIL::FPDenormMode::FTZ, L"ftz"},
-    {DXIL::FPDenormMode::Preserve, L"preserve"}
-  };
-
-  for (unsigned i = 0, end = modeMap.size(); i < end; ++i) {
-    std::vector<LPCWSTR> args(4);
-    args[0] = L"/denorm";
-    args[1] = modeMap[i].second;
-    args[2] = L"/T";
-    args[3] = L"ps_6_2";
-    Compiler c(m_dllSupport);
-    if (c.SkipDxil_Test(1, 2)) return;
-    c.Compile(
-        "float4 main() : SV_Target {\n"
-        "  return 0;\n"
-        "}\n"
-        ,
-        L"ps_6_2",
-        args, {}
-    );
-
-    DxilModule &DM = c.GetDxilModule();
-    DxilTypeSystem &TypeSystem = DM.GetTypeSystem();
-
-    // get main function
-    llvm::Module *M = DM.GetModule();
-    for (auto curFref = M->getFunctionList().begin(), endFref = M->getFunctionList().end();
-      curFref != endFref; ++curFref) {
-      DxilFunctionAnnotation *FuncAnnotation = TypeSystem.GetFunctionAnnotation(curFref);
-      llvm::StringRef name = curFref->getName();
-      if (FuncAnnotation == nullptr) {
-        VERIFY_IS_TRUE(name.startswith_lower("dx.op"));
-      }
-      else {
-        VERIFY_IS_TRUE(FuncAnnotation->GetFlag().GetFP32DenormMode() == modeMap[i].first);
-      }
-   }
-  }
-}
-
 TEST_F(DxilModuleTest, Precise1) {
   Compiler c(m_dllSupport);
   c.Compile(

+ 12 - 2
tools/clang/unittests/HLSL/ValidationTest.cpp

@@ -141,6 +141,7 @@ public:
   TEST_METHOD(WhenUnknownBlocksThenFail);
   TEST_METHOD(WhenZeroInputPatchCountWithInputThenFail);
 
+  TEST_METHOD(Float32DenormModeAttribute)
   TEST_METHOD(LoadOutputControlPointNotInPatchConstantFunction);
   TEST_METHOD(StorePatchControlNotInPatchConstantFunction);
   TEST_METHOD(OutputControlPointIDInPatchConstantFunction);
@@ -3118,6 +3119,15 @@ TEST_F(ValidationTest, BarycentricSamePerspectiveFail) {
       true);
 }
 
-
-
+TEST_F(ValidationTest, Float32DenormModeAttribute) {
+  if (m_ver.SkipDxilVersion(1, 2)) return;
+  std::vector<LPCWSTR> pArguments = { L"-denorm", L"ftz" };
+  RewriteAssemblyCheckMsg(
+    "float4 main(float4 col: COL) : SV_Target { return col; }", "ps_6_2",
+    pArguments.data(), 2, nullptr, 0,
+    { "\"fp32-denorm-mode\"=\"ftz\"" },
+    { "\"fp32-denorm-mode\"=\"invalid_mode\"" },
+    "contains invalid attribute 'fp32-denorm-mode' with value 'invalid_mode'",
+    true);
+}
 // TODO: reject non-zero padding

+ 4 - 4
utils/hct/hctdb.py

@@ -1445,7 +1445,7 @@ class db_dxil(object):
             (5, "Invalid", ""),
             ])
 
-        FPDenormMode = db_dxil_enum("FPDenormMode", "Floating point behavior", [
+        Float32DenormMode = db_dxil_enum("Float32DenormMode", "float32 denorm behavior", [
             (0, "Any", "Undefined behavior for denormal numbers"),
             (1, "Preserve", "Preserve both input and output"),
             (2, "FTZ", "Preserve denormal inputs. Flush denorm outputs"),
@@ -1455,7 +1455,7 @@ class db_dxil(object):
             (6, "Reserve6", "Reserved Value. Not used for now"),
             (7, "Reserve7", "Reserved Value. Not used for now"),
             ])
-        self.enums.append(FPDenormMode)
+        self.enums.append(Float32DenormMode)
 
 
         SigPointCSV = """
@@ -1596,7 +1596,6 @@ class db_dxil(object):
         self.add_valrule("Meta.BarycentricsInterpolation", "SV_Barycentrics cannot be used with 'nointerpolation' type")
         self.add_valrule("Meta.BarycentricsFloat3", "only 'float3' type is allowed for SV_Barycentrics.")
         self.add_valrule("Meta.BarycentricsTwoPerspectives", "There can only be up to two input attributes of SV_Barycentrics with different perspective interpolation mode.")
-        self.add_valrule("Meta.FPFlag", "Invalid funciton floating point flag.")
 
         self.add_valrule("Instr.Oload", "DXIL intrinsic overload must be valid")
         self.add_valrule_msg("Instr.CallOload", "Call to DXIL intrinsic must match overload signature", "Call to DXIL intrinsic '%0' does not match an allowed overload signature")
@@ -1764,7 +1763,8 @@ class db_dxil(object):
         self.add_valrule_msg("Decl.UsedExternalFunction", "External function must be used", "External function '%0' is unused")
         self.add_valrule_msg("Decl.FnIsCalled", "Functions can only be used by call instructions", "Function '%0' is used for something other than calling")
         self.add_valrule_msg("Decl.FnFlattenParam", "Function parameters must not use struct types", "Type '%0' is a struct type but is used as a parameter in function '%1'")
-        
+        self.add_valrule_msg("Decl.FnAttribute", "Functions should only contain known function attributes", "Function '%0' contains invalid attribute '%1' with value '%2'")
+
         # Assign sensible category names and build up an enumeration description
         cat_names = {
             "CONTAINER": "Container",