Explorar el Código

Support denorm option (#446)

This change is to support preserve denorm operations. We do this by creating dxc options and propagate them all the way to dxil metadata so that drivers can see how it should handle floats.
Currently we support three modes: any(default), preserve, and ftz

Denorm option will be of per function flag. We store this information at function annotation metadata. This means that for DXIL 1.2 we have a new structure for function annotation. The change in structure is documented on DXIL.rst
Young Kim hace 8 años
padre
commit
55667043c3

+ 116 - 27
docs/DXIL.rst

@@ -120,6 +120,18 @@ DXIL version must be declared exactly once per LLVM module (translation unit) an
 
 DXIL will evolve in a manner that retains backward compatibility.
 
+DXIL 1.1 Changes
+----------------
+Main two features that were introduced for DXIL1.1 (Shader Model 6.1) are view instancing and barycentric coordinates. Specifically, there are following changes to the DXIL representation.
+
+* New Intrinsics - AttributeAtVertex_, ViewID
+* New Systen Generated Value - SV_Barycentrics
+* New Container Part - ILDN
+
+DXIL 1.2 Changes
+----------------
+* New format for type-annotations_ for functions to indicate floating point operations behavior for per function basis.
+
 LLVM Bitcode version
 --------------------
 
@@ -386,41 +398,117 @@ HLSL precise type qualifier requires that all operations contributing to the val
 
 Precise behavior is represented in LLVM instructions: fadd, fsub, fmul, fdiv, frem, fcmp by not having 'fast' math flags set. Each relevant call instruction that contributes to computation of a precise value is annotated with dx.precise metadata that indicates that it is illegal for the driver compiler to perform IEEE-unsafe optimizations.
 
+.. _type-annotations:
+
 Type annotations
 ----------------
 
-User-defined types are annotated in DXIL to 'attach' additional properties to structure fields. For example, DXIL may contain type annotations for reflection purposes::
+User-defined types are annotated in DXIL to 'attach' additional properties to structure fields. For example, DXIL may contain type annotations of structures and funcitons for reflection purposes::
+
+  namespace MyNameSpace {
+    struct MyType {
+        float field1;
+        int2 field2;
+    };
+  }
+
+  float main(float col : COLOR) : SV_Target {
+    .....
+  }
+
+  !dx.typeAnnotations = !{!3, !7}
+  !3 = !{i32 0, %"struct.MyNameSpace::MyType" undef, !4}
+  !4 = !{i32 12, !5, !6}
+  !5 = !{i32 6, !"field1", i32 3, i32 0, i32 7, i32 9}
+  !6 = !{i32 6, !"field2", i32 3, i32 4, i32 7, i32 4}
+  !7 = !{i32 1, void (float, float*)* @"main", !8}
+  !8 = !{!9, !11, !14}
+  !9 = !{i32 0, !10, !10}
+  !10 = !{}
+  !11 = !{i32 0, !12, !13}
+  !12 = !{i32 4, !"COLOR", i32 7, i32 9}
+  !13 = !{i32 0}
+  !14 = !{i32 1, !15, !13}
+  !15 = !{i32 4, !"SV_Target", i32 7, i32 9}
+  !16 = !{null, !"lib.no::entry", null, null, null}
 
- ; namespace MyNamespace1
- ; {
- ;   struct MyType1
- ;   {
- ;     float field1;
- ;     int2 field2;
- ;   };
- ; }
+The type/field annotation metadata hierarchy recursively mimics LLVM type hierarchy.
+dx.typeAnnotations is a metadata of type annotation nodes, where each node represents type annotation of a certain type::
 
- %struct.MyNamespace1.MyType1 = type { float, <2 x i32> }
- !struct.MyNamespace1.MyType1 = !{ !1, !2 }
- !1 = !{ !"field1", null }
- !2 = !{ !"field2", null }
+  !dx.typeAnnotations = !{!3, !7}
 
- ; struct MyType2
- ; {
- ;    MyType1 array_field[2];
- ;    float4 float4_field;
- ; };
+For each **type annotation** node, the first value represents the type of the annotation::
 
- %struct.MyType2 = type { [2 x %struct.MyType1], <4 x float> }
- !struct.MyType2 = !{ !3, !4 }
- !3 = !{ !"array_field", null }
- !4 = !{ !"float4_field", null }
+  !3 = !{i32 0, %"struct.MyNameSpace::MyType" undef, !4}
+  !7 = !{i32 1, void (float, float*)* @"main", !8}
 
-The type/field annotation metadata hierarchy recursively mimics LLVM type hierarchy.
+=== =====================================================================
+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.
+
+**Structure Annotation** starts with the size of the structure in bytes, followed by the list of field annotations::
+
+  !4 = !{i32 12, !5, !6}
+  !5 = !{i32 6, !"field1", i32 3, i32 0, i32 7, i32 9}
+  !6 = !{i32 6, !"field2", i32 3, i32 4, i32 7, i32 4}
 
-Each field-annotation record has an optional named-value pair list for infrequent annotations and for future extensions. The lists are null in the example above.
+**Field Annotation** is a series of pairs with tag number followed by its value. Field Annotation pair is defined as follows
 
-Note that Clang emits '::' to separate namespaces, if any, in type names. We modify Clang to use '.' instead, because it is illegal to use ':' in metadata names.
+=== =====================================================================
+Idx Type
+=== =====================================================================
+0    SNorm
+1    UNorm
+2    Matrix
+3    Buffer Offset
+4    Semantic String
+5    Interpolation Mode
+6    Field Name
+7    Component Type
+8    Precise
+=== =====================================================================
+
+**Function Annotation** is a series of parameter annotations::
+
+  !7 = !{i32 1, void (float, float*)* @"main", !8}
+  !8 = !{!9, !11, !14}
+
+Each **Parameter Annotation** contains Input/Output type, field annotation, and semantic index::
+
+  !9 = !{i32 0, !10, !10}
+  !10 = !{}
+  !11 = !{i32 0, !12, !13}
+  !12 = !{i32 4, !"COLOR", i32 7, i32 9}
+  !13 = !{i32 0}
+  !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
 ==================================
@@ -1233,8 +1321,8 @@ CBufferLoadLegacy
 ~~~~~~~~~~~~~~~~~
 
 The following signature shows the operation syntax::
-  
-  ; overloads: SM5.1: f32|i32|f64,  future SM: possibly deprecated
+
+   ; overloads: SM5.1: f32|i32|f64,  future SM: possibly deprecated
   %dx.types.CBufRet.f32 = type { float, float, float, float }
   declare %dx.types.CBufRet.f32 @dx.op.cbufferLoadLegacy.f32(
       i32,                  ; opcode
@@ -2787,6 +2875,7 @@ 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

+ 15 - 0
include/dxc/HLSL/DxilConstants.h

@@ -209,6 +209,21 @@ namespace DXIL {
   };
   // PackingKind-ENUM:END
 
+  /* <py::lines('FPDenormMode-ENUM')>hctdb_instrhelp.get_enum_decl("FPDenormMode", hide_val=False, sort_val=False)</py>*/
+  // FPDenormMode-ENUM:BEGIN
+  // Floating point behavior
+  enum class FPDenormMode : unsigned {
+    Any = 0, // Undefined behavior for denormal numbers
+    Preserve = 1, // Preserve both input and output
+    FTZ = 2, // Preserve denormal inputs. Flush denorm outputs
+    Reserve3 = 3, // Reserved Value. Not used for now
+    Reserve4 = 4, // Reserved Value. Not used for now
+    Reserve5 = 5, // Reserved Value. Not used for now
+    Reserve6 = 6, // Reserved Value. Not used for now
+    Reserve7 = 7, // Reserved Value. Not used for now
+  };
+  // FPDenormMode-ENUM:END
+
   enum class PackingStrategy : unsigned {
     Default = 0, // Choose default packing algorithm based on target (currently PrefixStable)
     PrefixStable, // Maintain assumption that all elements are packed in order and stable as new elements are added.

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

@@ -47,6 +47,7 @@ class DxilStructAnnotation;
 class DxilFieldAnnotation;
 class DxilFunctionAnnotation;
 class DxilParameterAnnotation;
+class DxilFunctionFPFlag;
 class RootSignatureHandle;
 class DxilViewIdState;
 struct DxilFunctionProps;
@@ -170,7 +171,8 @@ public:
   static const char kDxilTypeSystemMDName[];
   static const char kDxilTypeSystemHelperVariablePrefix[];
   static const unsigned kDxilTypeSystemStructTag                  = 0;
-  static const unsigned kDxilTypeSystemFunctionTag                = 1;
+  static const unsigned kDxilTypeSystemFunctionTag                = 1; // For DXIL <= 1.1
+  static const unsigned kDxilTypeSystemFunction2Tag               = 2; // For DXIL >= 1.2
   static const unsigned kDxilFieldAnnotationSNormTag              = 0;
   static const unsigned kDxilFieldAnnotationUNormTag              = 1;
   static const unsigned kDxilFieldAnnotationMatrixTag             = 2;
@@ -333,8 +335,14 @@ 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,

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

@@ -138,6 +138,25 @@ 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;
@@ -149,10 +168,13 @@ 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.
@@ -171,6 +193,7 @@ 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 - 0
include/dxc/HLSL/DxilValidation.h

@@ -127,6 +127,7 @@ 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

+ 6 - 0
include/dxc/HLSL/HLModule.h

@@ -128,12 +128,17 @@ 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);
   DXIL::ResourceClass GetResourceClass(llvm::Type *Ty);
   DXIL::ResourceKind  GetResourceKind(llvm::Type *Ty);
 
+  // Float Denorm mode.
+  void SetFPDenormMode(const DXIL::FPDenormMode mode);
+  DXIL::FPDenormMode GetFPDenormMode() const;
+
   // HLDXIR metadata manipulation.
   /// Serialize HLDXIR in-memory form to metadata form.
   void EmitHLMetadata();
@@ -249,6 +254,7 @@ private:
   unsigned m_DxilMinor;
   unsigned m_ValMajor;
   unsigned m_ValMinor;
+  DXIL::FPDenormMode m_FPDenormMode;
   HLOptions m_Options;
   std::unique_ptr<OP> m_pOP;
   size_t m_pUnused;

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

@@ -108,6 +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
 
   bool AllResourcesBound; // OPT_all_resources_bound
   bool AstDump; // OPT_ast_dump

+ 2 - 0
include/dxc/Support/HLSLOptions.td

@@ -266,6 +266,8 @@ def Gfp : Flag<["-", "/"], "Gfp">, HelpText<"Prefer flow control constructs">, F
 def Ges : Flag<["-", "/"], "Ges">, HelpText<"Enable strict mode">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 def Gis : Flag<["-", "/"], "Gis">, HelpText<"Force IEEE strictness">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 
+def denorm : JoinedOrSeparate<["-", "/"], "denorm">, HelpText<"select denormal value options (any, preserve, ftz). any is the default.">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
+
 def Fo : JoinedOrSeparate<["-", "/"], "Fo">, MetaVarName<"<file>">, HelpText<"Output object file">, Flags<[DriverOption]>, Group<hlslcomp_Group>;
 // def Fl : JoinedOrSeparate<["-", "/"], "Fl">, MetaVarName<"<file>">, HelpText<"Output a library">;
 def Fc : JoinedOrSeparate<["-", "/"], "Fc">, MetaVarName<"<file>">, HelpText<"Output assembly code listing file">, Flags<[DriverOption]>, Group<hlslcomp_Group>;

+ 18 - 1
lib/DxcSupport/HLSLOptions.cpp

@@ -17,6 +17,7 @@
 #include "dxc/Support/HLSLOptions.h"
 #include "dxc/Support/Unicode.h"
 #include "dxc/Support/dxcapi.use.h"
+#include "dxc/HLSL/DxilShaderModel.h"
 
 using namespace llvm::opt;
 using namespace dxc;
@@ -297,7 +298,23 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
   }
 
   opts.IEEEStrict = Args.hasFlag(OPT_Gis, OPT_INVALID, false);
-  
+
+  opts.FPDenormalMode = 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
+          << "' for denorm option.";
+      return 1;
+    }
+    if (opts.TargetProfile.empty() || !opts.TargetProfile.endswith_lower("6_2")) {
+      errors << "denorm option is only allowed for shader model 6.2 and above.";
+      return 1;
+    }
+  }
+
   if (Arg *A = Args.getLastArg(OPT_O0, OPT_O1, OPT_O2, OPT_O3)) {
     if (A->getOption().matches(OPT_O0))
       opts.OptLevel = 0;

+ 0 - 1
lib/HLSL/DxilGenerationPass.cpp

@@ -1302,7 +1302,6 @@ ModulePass *llvm::createHLEnsureMetadataPass() {
 
 INITIALIZE_PASS(HLEnsureMetadata, "hlsl-hlensure", "HLSL High-Level Metadata Ensure", false, false)
 
-
 ///////////////////////////////////////////////////////////////////////////////
 // Precise propagate.
 

+ 74 - 14
lib/HLSL/DxilMetadataHelper.cpp

@@ -64,7 +64,7 @@ static std::array<const char *, 7> DxilMDNames = {
   DxilMDHelper::kDxilResourcesMDName,
   DxilMDHelper::kDxilTypeSystemMDName,
   DxilMDHelper::kDxilValidatorVersionMDName,
-  DxilMDHelper::kDxilViewIdStateMDName
+  DxilMDHelper::kDxilViewIdStateMDName,
 };
 
 DxilMDHelper::DxilMDHelper(Module *pModule, std::unique_ptr<ExtraPropertyHelper> EPH)
@@ -696,14 +696,27 @@ void DxilMDHelper::EmitDxilTypeSystem(DxilTypeSystem &TypeSystem, vector<GlobalV
 
   auto &FuncMap = TypeSystem.GetFunctionAnnotationMap();
   vector<Metadata *> MDFuncVals;
-  MDFuncVals.emplace_back(Uint32ToConstMD(kDxilTypeSystemFunctionTag)); // Tag
-
+  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));
+  }
   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 = EmitDxilFunctionAnnotation(*pA);
 
+   Metadata *pMD;
+    if (major == 1 && minor <= 1) {
+      pMD = EmitDxilFunctionAnnotation(*pA);
+    }
+    else {
+      pMD = EmitDxilFunctionAnnotation2(*pA);
+    }
     MDFuncVals.push_back(pMD);
   }
 
@@ -741,14 +754,20 @@ void DxilMDHelper::LoadDxilTypeSystemNode(const llvm::MDTuple &MDT,
       DxilStructAnnotation *pSA = TypeSystem.AddStructAnnotation(pGVType);
       LoadDxilStructAnnotation(MDT.getOperand(i + 1), *pSA);
     }
-  } else {
-    IFTBOOL(Tag == kDxilTypeSystemFunctionTag, DXC_E_INCORRECT_DXIL_METADATA);
+  } else if (Tag == kDxilTypeSystemFunctionTag) {
     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);
+    }
   }
 }
 
@@ -798,24 +817,51 @@ 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) {
-  vector<Metadata *> MDVals(FA.GetNumParameters() + 1);
-  MDVals[0] = EmitDxilParamAnnotation(FA.GetRetTypeAnnotation());
-  for (unsigned i = 0; i < FA.GetNumParameters(); i++) {
-    MDVals[i + 1] = EmitDxilParamAnnotation(FA.GetParameterAnnotation(i));
-  }
-
-  return MDNode::get(m_Ctx, MDVals);
+  return EmitDxilParamAnnotations(FA);
 }
+
 void DxilMDHelper::LoadDxilFunctionAnnotation(const MDOperand &MDO,
                                               DxilFunctionAnnotation &FA) {
+  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);
+  MDParamAnnotations[0] = EmitDxilParamAnnotation(FA.GetRetTypeAnnotation());
+  for (unsigned i = 0; i < FA.GetNumParameters(); i++) {
+    MDParamAnnotations[i + 1] =
+        EmitDxilParamAnnotation(FA.GetParameterAnnotation(i));
+  }
+  return MDNode::get(m_Ctx, MDParamAnnotations);
+}
+
+void DxilMDHelper::LoadDxilParamAnnotations(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->getNumOperands() == FA.GetNumParameters() + 1,
           DXC_E_INCORRECT_DXIL_METADATA);
-
   DxilParameterAnnotation &retTyAnnotation = FA.GetRetTypeAnnotation();
   LoadDxilParamAnnotation(pTupleMD->getOperand(0), retTyAnnotation);
   for (unsigned i = 0; i < FA.GetNumParameters(); i++) {
@@ -824,6 +870,20 @@ void DxilMDHelper::LoadDxilFunctionAnnotation(const MDOperand &MDO,
     LoadDxilParamAnnotation(MDO, PA);
   }
 }
+
+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);

+ 4 - 2
lib/HLSL/DxilPreparePasses.cpp

@@ -131,10 +131,12 @@ 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().AddFunctionAnnotation(NewFunc);
+  DM.GetTypeSystem().AddFunctionAnnotationWithFPFlag(NewFunc, &flag);
   return NewFunc;
 }
 

+ 42 - 1
lib/HLSL/DxilTypeSystem.cpp

@@ -156,6 +156,39 @@ 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.
@@ -204,11 +237,19 @@ 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;
 }
 
@@ -312,7 +353,7 @@ void DxilTypeSystem::CopyFunctionAnnotation(const llvm::Function *pDstFunction,
   if (GetFunctionAnnotation(pDstFunction))
     return;
 
-  DxilFunctionAnnotation *dstAnnot = AddFunctionAnnotation(pDstFunction);
+  DxilFunctionAnnotation *dstAnnot = AddFunctionAnnotationWithFPFlag(pDstFunction, &src.GetFunctionAnnotation(pSrcFunction)->GetFlag());
 
   // Copy the annotation.
   *dstAnnot = *annot;

+ 43 - 0
lib/HLSL/DxilValidation.cpp

@@ -108,6 +108,7 @@ 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";
@@ -2768,6 +2769,47 @@ static void ValidateDxilVersion(ValidationContext &ValCtx) {
   ValCtx.EmitError(ValidationRule::MetaWellFormed);
 }
 
+static void ValidateTypeAnnotation(ValidationContext &ValCtx) {
+  if (ValCtx.m_DxilMajor == 1 && ValCtx.m_DxilMinor >= 2) {
+    Module *pModule = &ValCtx.M;
+    NamedMDNode *TA = pModule->getNamedMetadata("dx.typeAnnotations");
+    if (TA == nullptr)
+      return;
+    for (unsigned i = 0, end = TA->getNumOperands(); i < end; ++i) {
+      MDTuple *TANode = dyn_cast<MDTuple>(TA->getOperand(i));
+      if (TANode->getNumOperands() < 3) {
+        ValCtx.EmitMetaError(TANode, ValidationRule::MetaWellFormed);
+        return;
+      }
+      ConstantInt *tag = mdconst::extract<ConstantInt>(TANode->getOperand(0));
+      uint64_t tagValue = tag->getZExtValue();
+      if (tagValue != DxilMDHelper::kDxilTypeSystemStructTag &&
+          tagValue != DxilMDHelper::kDxilTypeSystemFunctionTag &&
+          tagValue != DxilMDHelper::kDxilTypeSystemFunction2Tag) {
+          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);
+            }
+          }
+      }
+    }
+  }
+}
+
 static void ValidateMetadata(ValidationContext &ValCtx) {
   Module *pModule = &ValCtx.M;
   const std::string &target = pModule->getTargetTriple();
@@ -2817,6 +2859,7 @@ static void ValidateMetadata(ValidationContext &ValCtx) {
 
   ValidateDxilVersion(ValCtx);
   ValidateValidatorVersion(ValCtx);
+  ValidateTypeAnnotation(ValCtx);
 }
 
 static void ValidateResourceOverlap(

+ 15 - 0
lib/HLSL/HLModule.cpp

@@ -350,6 +350,13 @@ 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) {
@@ -384,6 +391,14 @@ static unsigned GetFloatAt(MDTuple *tuple, unsigned idx) {
   return DxilMDHelper::ConstMDToFloat(tuple->getOperand(idx));
 }
 
+DXIL::FPDenormMode HLModule::GetFPDenormMode() const {
+  return m_FPDenormMode;
+}
+
+void HLModule::SetFPDenormMode(const DXIL::FPDenormMode mode) {
+  m_FPDenormMode = mode;
+}
+
 static const StringRef kHLDxilFunctionPropertiesMDName           = "dx.fnprops";
 static const StringRef kHLDxilOptionsMDName                      = "dx.options";
 static const StringRef kHLDxilResourceTypeAnnotationMDName       = "dx.resource.type.annotation";

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

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

+ 2 - 0
tools/clang/include/clang/Frontend/CodeGenOptions.h

@@ -198,6 +198,8 @@ public:
   std::shared_ptr<hlsl::HLSLExtensionsCodegenHelper> HLSLExtensionsCodegen;
   /// Signature packing mode (0 == default for target)
   unsigned HLSLSignaturePackingStrategy = 0;
+  /// denormalized number mode ("ieee" for default)
+  hlsl::DXIL::FPDenormMode HLSLFlushFPDenorm;
   // HLSL Change Ends
   /// Regular expression to select optimizations for which we should enable
   /// optimization remarks. Transformation passes whose name matches this

+ 5 - 2
tools/clang/lib/CodeGen/CGHLSLMS.cpp

@@ -378,6 +378,9 @@ CGMSHLSLRuntime::CGMSHLSLRuntime(CodeGenModule &CGM)
   CB->SetRangeSize(1);
   CB->SetLowerBound(UINT_MAX);
   DXVERIFY_NOMSG(globalCBIndex == m_pHLModule->AddCBuffer(std::move(CB)));
+
+  // set Float Denorm Mode
+  m_pHLModule->SetFPDenormMode(CGM.getCodeGenOpts().HLSLFlushFPDenorm);
 }
 
 bool CGMSHLSLRuntime::IsHlslObjectType(llvm::Type *Ty) {
@@ -1370,7 +1373,7 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) {
   }
 
   DxilFunctionAnnotation *FuncAnnotation =
-      m_pHLModule->AddFunctionAnnotation(F);
+      m_pHLModule->AddFunctionAnnotationWithFPDenormMode(F, m_pHLModule->GetFPDenormMode());
   bool bDefaultRowMajor = m_pHLModule->GetHLOptions().bDefaultRowMajor;
 
   // Param Info
@@ -3850,7 +3853,7 @@ static void CloneShaderEntry(Function *ShaderF, StringRef EntryName,
 
   // Copy function annotation.
   DxilFunctionAnnotation *shaderAnnot = HLM.GetFunctionAnnotation(ShaderF);
-  DxilFunctionAnnotation *annot = HLM.AddFunctionAnnotation(F);
+  DxilFunctionAnnotation *annot = HLM.AddFunctionAnnotationWithFPDenormMode(F, HLM.GetFPDenormMode());
 
   DxilParameterAnnotation &retAnnot = shaderAnnot->GetRetTypeAnnotation();
   DxilParameterAnnotation &cloneRetAnnot = annot->GetRetTypeAnnotation();

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

@@ -792,6 +792,17 @@ public:
     if (Opts.IEEEStrict)
       compiler.getCodeGenOpts().UnsafeFPMath = true;
 
+    if (Opts.FPDenormalMode.empty() || Opts.FPDenormalMode.equals_lower(StringRef("any"))) {
+      compiler.getCodeGenOpts().HLSLFlushFPDenorm = DXIL::FPDenormMode::Any;
+    }
+    else if (Opts.FPDenormalMode.equals_lower(StringRef("ftz"))) {
+      compiler.getCodeGenOpts().HLSLFlushFPDenorm = DXIL::FPDenormMode::FTZ;
+    }
+    else {
+      DXASSERT(Opts.FPDenormalMode.equals_lower(StringRef("preserve")), "else opts should have been rejected");
+      compiler.getCodeGenOpts().HLSLFlushFPDenorm = DXIL::FPDenormMode::Preserve;
+    }
+
     if (Opts.DisableOptimizations)
       compiler.getCodeGenOpts().DisableLLVMOpts = true;
 

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

@@ -48,6 +48,8 @@ public:
   TEST_METHOD(LoadDxilModule_1_1);
   TEST_METHOD(LoadDxilModule_1_2);
 
+  TEST_METHOD(FunctionFPFlag);
+
   // Precise query tests.
   TEST_METHOD(Precise1);
   TEST_METHOD(Precise2);
@@ -216,6 +218,49 @@ 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(

+ 14 - 0
utils/hct/hctdb.py

@@ -1432,6 +1432,19 @@ class db_dxil(object):
             (5, "Invalid", ""),
             ])
 
+        FPDenormMode = db_dxil_enum("FPDenormMode", "Floating point behavior", [
+            (0, "Any", "Undefined behavior for denormal numbers"),
+            (1, "Preserve", "Preserve both input and output"),
+            (2, "FTZ", "Preserve denormal inputs. Flush denorm outputs"),
+            (3, "Reserve3", "Reserved Value. Not used for now"),
+            (4, "Reserve4", "Reserved Value. Not used for now"),
+            (5, "Reserve5", "Reserved Value. Not used for now"),
+            (6, "Reserve6", "Reserved Value. Not used for now"),
+            (7, "Reserve7", "Reserved Value. Not used for now"),
+            ])
+        self.enums.append(FPDenormMode)
+
+
         SigPointCSV = """
             SigPoint, Related, ShaderKind, PackingKind,    SignatureKind
             VSIn,     Invalid, Vertex,     InputAssembler, Input
@@ -1569,6 +1582,7 @@ 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")

+ 27 - 1
utils/hct/hcttestcmds.cmd

@@ -523,6 +523,33 @@ if %errorlevel% neq 0 (
   exit /b 1
 )
 
+dxc.exe smoke.hlsl /Tps_6_2 /denorm preserve 1>nul
+if %errorlevel% neq 0 (
+  echo Failed to compile smoke.hlsl with /denorm ieee option
+  call :cleanup 2>nul
+  exit /b 1
+)
+
+dxc.exe smoke.hlsl /Tps_6_2 /denorm ftz 1>nul
+if %errorlevel% neq 0 (
+  echo Failed to compile smoke.hlsl with /denorm ftz option
+  call :cleanup 2>nul
+  exit /b 1
+)
+
+dxc.exe smoke.hlsl /Tps_6_2 /denorm abc 2>nul
+if %errorlevel% equ 0 (
+  echo dxc incorrectly compiled smoke.hlsl with invalid /denorm option
+  call :cleanup 2>nul
+  exit /b 1
+)
+
+dxc.exe smoke.hlsl /Tps_6_1 /denorm any 2>nul
+if %errorlevel% equ 0 (
+  echo dxc incorrectly compiled smoke.hlsl shader model 6.1 with /denorm option
+  call :cleanup 2>nul
+  exit /b 1
+)
 
 call :cleanup
 exit /b 0
@@ -558,6 +585,5 @@ del %CD%\smoke.opt.prn.txt
 del %CD%\smoke.rebuilt-container.cso
 del %CD%\smoke.rebuilt-container2.cso
 
-
 exit /b 0