https://github.com/spall created 
https://github.com/llvm/llvm-project/pull/232534

Add support for a simplified version of precise; if precise appears anywhere in 
the translation unit then use IEEE-strict math mode.

Assisted by Claude Opus 5.5 and GTP Sol 6.1 

Closes #232058

>From 0390bceda873df75357e7890a638023de5359559 Mon Sep 17 00:00:00 2001
From: Sarah Spall <[email protected]>
Date: Wed, 14 Oct 2026 16:38:26 -0700
Subject: [PATCH 1/2] implement precise keyword and use ieee strict if precise
 appears in tu

---
 clang/include/clang/AST/ASTContext.h       |  5 ++
 clang/include/clang/Basic/Attr.td          |  7 ++
 clang/include/clang/Basic/AttrDocs.td      | 17 +++++
 clang/include/clang/Basic/TokenKinds.def   |  1 +
 clang/include/clang/Sema/SemaHLSL.h        |  1 +
 clang/lib/CodeGen/ModuleBuilder.cpp        | 87 +++++++++++++++++++++-
 clang/lib/Parse/ParseDecl.cpp              |  4 +
 clang/lib/Parse/ParseTentative.cpp         |  3 +-
 clang/lib/Sema/SemaDeclAttr.cpp            |  3 +
 clang/lib/Sema/SemaHLSL.cpp                |  8 ++
 clang/test/CodeGenHLSL/precise.hlsl        | 38 ++++++++++
 clang/test/ParserHLSL/precise-keyword.hlsl |  6 ++
 clang/test/SemaHLSL/precise-ast.hlsl       | 34 +++++++++
 clang/test/SemaHLSL/precise-errors.hlsl    |  4 +
 14 files changed, 215 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/precise.hlsl
 create mode 100644 clang/test/ParserHLSL/precise-keyword.hlsl
 create mode 100644 clang/test/SemaHLSL/precise-ast.hlsl
 create mode 100644 clang/test/SemaHLSL/precise-errors.hlsl

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 1349637273d1e7..ba78877ddf4948 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -325,6 +325,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   mutable llvm::ContextualFoldingSet<HLSLAttributedResourceType, ASTContext &>
       HLSLAttributedResourceTypes;
   llvm::FoldingSet<HLSLInlineSpirvType> HLSLInlineSpirvTypes;
+  /// Whether the HLSL `precise` keyword appears in this translation unit.
+  bool HLSLPreciseUsed = false;
 
   mutable llvm::FoldingSet<CountAttributedType> CountAttributedTypes;
 
@@ -2103,6 +2105,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                   uint32_t Alignment,
                                   ArrayRef<SpirvOperand> Operands);
 
+  void setHLSLPreciseUsed() { HLSLPreciseUsed = true; }
+  bool isHLSLPreciseUsed() const { return HLSLPreciseUsed; }
+
   QualType getSubstTemplateTypeParmType(QualType Replacement,
                                         Decl *AssociatedDecl, unsigned Index,
                                         UnsignedOrNone PackIndex,
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index f8bd097733529a..5b4074fa184d3f 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5396,6 +5396,13 @@ def HLSLInterpolationModifier : InheritableAttr {
   let Documentation = [HLSLInterpolationModifierDocs];
 }
 
+def HLSLPrecise : InheritableAttr {
+  let Spellings = [CustomKeyword<"precise">];
+  let Subjects = SubjectList<[Var, Field, Function], ErrorDiag>;
+  let LangOpts = [HLSL];
+  let Documentation = [HLSLPreciseDocs];
+}
+
 def HLSLParamModifier : ParameterABIAttr {
   let Spellings = [CustomKeyword<"in">, CustomKeyword<"inout">, 
CustomKeyword<"out">];
   let Accessors = [Accessor<"isIn", [CustomKeyword<"in">]>,
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index e8ce3c3dea7f3b..a6798322c52b72 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -9434,6 +9434,23 @@ interpolation mode; their interpolation modifiers are 
ignored.
   }];
 }
 
+def HLSLPreciseDocs : Documentation {
+  let Category = DocCatVariable;
+  let Heading = "precise";
+  let Content = [{
+The HLSL ``precise`` keyword can be applied to variables, parameters, fields,
+and function return values to request that floating-point computations
+contributing to their values are performed with IEEE-strict semantics.
+
+Clang currently implements a simplified version of ``precise``: if the keyword
+is used anywhere in a translation unit, the whole translation unit is compiled
+in IEEE-strict floating-point mode, as if ``-Gis`` (``-ffp-model=strict``) had
+been specified.
+
+The full documentation is available here: 
https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-syntax
+  }];
+}
+
 def HLSLParamQualifierDocs : Documentation {
   let Category = DocCatVariable;
   let Heading = "HLSL Parameter Modifiers";
diff --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index 528825ad68b8dc..318584cbd26df2 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -550,6 +550,7 @@ KEYWORD(centroid                    , KEYHLSL)
 KEYWORD(noperspective               , KEYHLSL)
 KEYWORD(sample                      , KEYHLSL)
 KEYWORD(center                      , KEYHLSL)
+KEYWORD(precise                     , KEYHLSL)
 KEYWORD(in                          , KEYHLSL)
 KEYWORD(inout                       , KEYHLSL)
 KEYWORD(out                         , KEYHLSL)
diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 3d62f47dfde691..01ade1295a8670 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -192,6 +192,7 @@ class SemaHLSL : public SemaBase {
   void handleResourceBindingAttr(Decl *D, const ParsedAttr &AL);
   void handleParamModifierAttr(Decl *D, const ParsedAttr &AL);
   void handleInterpolationModifierAttr(Decl *D, const ParsedAttr &AL);
+  void handlePreciseAttr(Decl *D, const ParsedAttr &AL);
   Attr *buildMatrixLayoutTypeAttr(QualType T, const ParsedAttr &AL);
   bool diagnoseMatrixLayoutInstantiation(attr::Kind K, QualType T,
                                          SourceLocation Loc);
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp 
b/clang/lib/CodeGen/ModuleBuilder.cpp
index 0b00362487d2af..a3bd38b7824099 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -26,6 +26,7 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include "llvm/Support/VirtualFileSystem.h"
 #include <memory>
 
@@ -66,6 +67,22 @@ namespace {
     std::unique_ptr<CodeGen::CodeGenModule> Builder;
     SmallVector<FunctionDecl *, 8> DeferredInlineMemberFuncDefs;
 
+    /// HLSL defers all code generation to the end of the translation unit
+    /// because a use of the `precise` keyword anywhere in it changes the
+    /// floating-point mode of the whole translation unit. The deferred
+    /// callbacks are replayed in their original order.
+    struct DeferredHLSLDecl {
+      enum KindTy { TopLevel, InlineFunction, StaticMemberVar } Kind;
+      DeclGroupRef DG;
+    };
+    SmallVector<DeferredHLSLDecl, 0> DeferredHLSLDecls;
+    bool EmittingDeferredHLSLDecls = false;
+
+    bool shouldDeferForHLSL() const {
+      return Ctx && Ctx->getLangOpts().HLSL && !IRGenFinished &&
+             !EmittingDeferredHLSLDecls;
+    }
+
     static llvm::StringRef ExpandModuleName(llvm::StringRef ModuleName,
                                             const CodeGenOptions &CGO) {
       if (ModuleName == "-" && !CGO.MainFileName.empty())
@@ -182,6 +199,12 @@ namespace {
       if (Diags.hasErrorOccurred())
         return;
 
+      if (shouldDeferForHLSL()) {
+        DeferredHLSLDecls.push_back(
+            {DeferredHLSLDecl::StaticMemberVar, DeclGroupRef(VD)});
+        return;
+      }
+
       Builder->HandleCXXStaticMemberVarInstantiation(VD);
     }
 
@@ -194,13 +217,63 @@ namespace {
       if (Diags.hasUnrecoverableErrorOccurred())
         return true;
 
+      if (shouldDeferForHLSL()) {
+        DeferredHLSLDecls.push_back({DeferredHLSLDecl::TopLevel, DG});
+        return true;
+      }
+
+      EmitTopLevelDeclGroup(DG);
+      return true;
+    }
+
+    void EmitTopLevelDeclGroup(DeclGroupRef DG) {
       HandlingTopLevelDeclRAII HandlingDecl(*this);
 
       // Make sure to emit all elements of a Decl.
       for (auto &I : DG)
         Builder->EmitTopLevelDecl(I);
+    }
 
-      return true;
+    /// Clang implements a simplified version of the HLSL `precise` keyword: if
+    /// it is used anywhere in the translation unit, the whole translation unit
+    /// is compiled in IEEE-strict mode, matching -Gis (-ffp-model=strict).
+    void applyHLSLPreciseFPMode() {
+      if (!Ctx->isHLSLPreciseUsed())
+        return;
+      // ASTContext refers to the CompilerInvocation's mutable LangOptions.
+      LangOptions &LO = const_cast<LangOptions &>(Ctx->getLangOpts());
+      LO.setDefaultFPContractMode(LangOptions::FPM_Off);
+      LO.setComplexRange(LangOptions::CX_Full);
+      // Like CompilerInstance::createTarget, only enable the rounding and
+      // exception modes on targets that support them.
+      if (Ctx->getTargetInfo().hasStrictFP() || LO.ExpStrictFP) {
+        LO.RoundingMath = true;
+        LO.setFPExceptionMode(LangOptions::FPE_Strict);
+      }
+    }
+
+    void EmitDeferredHLSLDecls() {
+      // Callbacks triggered while emitting are handled directly.
+      llvm::SaveAndRestore Emitting(EmittingDeferredHLSLDecls, true);
+      for (DeferredHLSLDecl &D : DeferredHLSLDecls) {
+        if (Diags.hasUnrecoverableErrorOccurred())
+          return;
+        switch (D.Kind) {
+        case DeferredHLSLDecl::TopLevel:
+          EmitTopLevelDeclGroup(D.DG);
+          break;
+        case DeferredHLSLDecl::InlineFunction:
+          DeferredInlineMemberFuncDefs.push_back(
+              cast<FunctionDecl>(D.DG.getSingleDecl()));
+          break;
+        case DeferredHLSLDecl::StaticMemberVar:
+          Builder->HandleCXXStaticMemberVarInstantiation(
+              cast<VarDecl>(D.DG.getSingleDecl()));
+          break;
+        }
+      }
+      DeferredHLSLDecls.clear();
+      EmitDeferredDecls();
     }
 
     void EmitDeferredDecls() {
@@ -230,7 +303,11 @@ namespace {
       //     void bar();
       //     void foo() { bar(); }
       //   } A;
-      DeferredInlineMemberFuncDefs.push_back(D);
+      if (shouldDeferForHLSL())
+        DeferredHLSLDecls.push_back(
+            {DeferredHLSLDecl::InlineFunction, DeclGroupRef(D)});
+      else
+        DeferredInlineMemberFuncDefs.push_back(D);
 
       // Provide some coverage mapping even for methods that aren't emitted.
       // Don't do this for templated classes though, as they may not be
@@ -310,6 +387,12 @@ namespace {
     }
 
     void HandleTranslationUnit(ASTContext &Ctx) override {
+      if (!Diags.hasUnrecoverableErrorOccurred() && Builder &&
+          shouldDeferForHLSL()) {
+        applyHLSLPreciseFPMode();
+        EmitDeferredHLSLDecls();
+      }
+
       // Release the Builder when there is no error.
       if (!Diags.hasUnrecoverableErrorOccurred() && Builder)
         Builder->Release();
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 40249287184446..6ed82d5cdc49ff 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4702,6 +4702,7 @@ void Parser::ParseDeclarationSpecifiers(
     case tok::kw_noperspective:
     case tok::kw_sample:
     case tok::kw_center:
+    case tok::kw_precise:
     case tok::kw_groupshared:
     case tok::kw_in:
     case tok::kw_inout:
@@ -5873,6 +5874,7 @@ bool Parser::isTypeSpecifierQualifier(const Token &Tok) {
   case tok::kw_noperspective:
   case tok::kw_sample:
   case tok::kw_center:
+  case tok::kw_precise:
   case tok::kw_groupshared:
   case tok::kw_in:
   case tok::kw_inout:
@@ -6167,6 +6169,7 @@ bool Parser::isDeclarationSpecifier(
   case tok::kw_noperspective:
   case tok::kw_sample:
   case tok::kw_center:
+  case tok::kw_precise:
   case tok::kw_row_major:
   case tok::kw_column_major:
     return getLangOpts().HLSL;
@@ -6419,6 +6422,7 @@ void Parser::ParseTypeQualifierListOpt(
     case tok::kw_noperspective:
     case tok::kw_sample:
     case tok::kw_center:
+    case tok::kw_precise:
       // NOTE: ParseHLSLQualifiers will consume the qualifier token.
       ParseHLSLQualifiers(DS.getAttributes());
       continue;
diff --git a/clang/lib/Parse/ParseTentative.cpp 
b/clang/lib/Parse/ParseTentative.cpp
index a4581538fbcec1..11e38f5ec54e8f 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1286,13 +1286,14 @@ 
Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
     // OpenCL pipe
   case tok::kw_pipe:
 
-    // HLSL interpolation and address space qualifiers
+    // HLSL interpolation, precise, and address space qualifiers
   case tok::kw_nointerpolation:
   case tok::kw_linear:
   case tok::kw_centroid:
   case tok::kw_noperspective:
   case tok::kw_sample:
   case tok::kw_center:
+  case tok::kw_precise:
   case tok::kw_groupshared:
   case tok::kw_in:
   case tok::kw_inout:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 33b680c4d93d4c..8292e2372d2a44 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -8328,6 +8328,9 @@ ProcessDeclAttribute(Sema &S, Decl *D, const ParsedAttr 
&AL,
   case ParsedAttr::AT_HLSLInterpolationModifier:
     S.HLSL().handleInterpolationModifierAttr(D, AL);
     break;
+  case ParsedAttr::AT_HLSLPrecise:
+    S.HLSL().handlePreciseAttr(D, AL);
+    break;
   case ParsedAttr::AT_HLSLParamModifier:
     S.HLSL().handleParamModifierAttr(D, AL);
     break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 4fe7ed151e9180..716ab3d7d2a028 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -827,6 +827,14 @@ SemaHLSL::mergeParamModifierAttr(Decl *D, const 
AttributeCommonInfo &AL,
   return HLSLParamModifierAttr::Create(getASTContext(), AL);
 }
 
+void SemaHLSL::handlePreciseAttr(Decl *D, const ParsedAttr &AL) {
+  // Only a simplified form of precise is supported: any use of the keyword
+  // puts the whole translation unit in IEEE-strict floating-point mode.
+  getASTContext().setHLSLPreciseUsed();
+  if (!D->hasAttr<HLSLPreciseAttr>())
+    D->addAttr(HLSLPreciseAttr::Create(getASTContext(), AL));
+}
+
 void SemaHLSL::handleInterpolationModifierAttr(Decl *D, const ParsedAttr &AL) {
   InterpolationModifier Modifier;
   switch (static_cast<HLSLInterpolationModifierAttr::Spelling>(
diff --git a/clang/test/CodeGenHLSL/precise.hlsl 
b/clang/test/CodeGenHLSL/precise.hlsl
new file mode 100644
index 00000000000000..e43e2d179568bc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/precise.hlsl
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,PRECISE
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-emit-llvm -disable-llvm-passes -o - %s -DNO_PRECISE | FileCheck %s 
--check-prefixes=CHECK,DEFAULT
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -ffp-contract=on 
-emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,PRECISE
+
+// A simplified version of precise is implemented: any use of the keyword in a
+// translation unit puts the whole translation unit in IEEE-strict mode, which
+// disables floating-point contraction. This includes code emitted before the
+// keyword is seen.
+
+// CHECK-LABEL: define {{.*}}float @_Z6Beforeff(
+// PRECISE: %[[MUL:.*]] = fmul {{.*}}float
+// PRECISE: fadd {{.*}}float %[[MUL]],
+// PRECISE-NOT: fmuladd
+// DEFAULT: call {{.*}}float @llvm.fmuladd.f32(
+export float Before(float A, float B) { return A * B + A; }
+
+float Two() { return 2.0; }
+// The dynamic initializer of a global is also emitted in strict mode.
+// CHECK-LABEL: define internal {{.*}}void @__cxx_global_var_init(
+// PRECISE: %[[MUL:.*]] = fmul {{.*}}float
+// PRECISE: fadd {{.*}}float %[[MUL]],
+// PRECISE-NOT: fmuladd
+// DEFAULT: call {{.*}}float @llvm.fmuladd.f32(
+static float G = Two() * Two() + Two();
+
+// CHECK-LABEL: define {{.*}}float @_Z5Afterf(
+// PRECISE: %[[MUL:.*]] = fmul {{.*}}float
+// PRECISE: fadd {{.*}}float %[[MUL]],
+// PRECISE-NOT: fmuladd
+// DEFAULT: call {{.*}}float @llvm.fmuladd.f32(
+export float After(float A) {
+#ifdef NO_PRECISE
+  float X = A * A + A;
+#else
+  precise float X = A * A + A;
+#endif
+  return X;
+}
diff --git a/clang/test/ParserHLSL/precise-keyword.hlsl 
b/clang/test/ParserHLSL/precise-keyword.hlsl
new file mode 100644
index 00000000000000..99d8611770f0e1
--- /dev/null
+++ b/clang/test/ParserHLSL/precise-keyword.hlsl
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -x c++ -verify %s
+// RUN: %clang_cc1 -x c -verify %s
+// expected-no-diagnostics
+
+// precise is a keyword only in HLSL.
+int precise;
diff --git a/clang/test/SemaHLSL/precise-ast.hlsl 
b/clang/test/SemaHLSL/precise-ast.hlsl
new file mode 100644
index 00000000000000..7cd763f755ba84
--- /dev/null
+++ b/clang/test/SemaHLSL/precise-ast.hlsl
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ast-dump %s | 
FileCheck %s
+
+struct S {
+  precise float F;
+};
+// CHECK-LABEL: CXXRecordDecl {{.*}} struct S definition
+// CHECK: FieldDecl {{.*}} F 'float'
+// CHECK-NEXT: HLSLPreciseAttr
+
+precise float Fn(precise float P) {
+  precise float L = P;
+  float precise M = L;
+  return M;
+}
+// CHECK-LABEL: FunctionDecl {{.*}} Fn 'float (float)'
+// CHECK: ParmVarDecl {{.*}} used P 'float'
+// CHECK-NEXT: HLSLPreciseAttr
+// CHECK: VarDecl {{.*}} used L 'float' cinit
+// CHECK: HLSLPreciseAttr
+// CHECK: VarDecl {{.*}} used M 'float' cinit
+// CHECK: HLSLPreciseAttr
+// CHECK: HLSLPreciseAttr
+
+template <typename T> T Tmpl(T V) {
+  precise T X = V;
+  return X;
+}
+float Inst() { return Tmpl<float>(1.0); }
+// CHECK-LABEL: FunctionTemplateDecl {{.*}} Tmpl
+// CHECK: VarDecl {{.*}} X 'T' nrvo cinit
+// CHECK: HLSLPreciseAttr
+// CHECK: FunctionDecl {{.*}} used Tmpl 'float (float)' implicit_instantiation
+// CHECK: VarDecl {{.*}} X 'float' cinit
+// CHECK: HLSLPreciseAttr
diff --git a/clang/test/SemaHLSL/precise-errors.hlsl 
b/clang/test/SemaHLSL/precise-errors.hlsl
new file mode 100644
index 00000000000000..ea1340c530e2a5
--- /dev/null
+++ b/clang/test/SemaHLSL/precise-errors.hlsl
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -verify %s
+
+// expected-error@+1 {{'precise' attribute only applies to variables, 
non-static data members, and functions}}
+typedef precise float PreciseFloat;

>From a44e98175538786cc1b3f1d1be49629b021ec71c Mon Sep 17 00:00:00 2001
From: Sarah Spall <[email protected]>
Date: Thu, 15 Oct 2026 15:22:32 -0700
Subject: [PATCH 2/2] update from copilot review

---
 clang/include/clang/Basic/LangOptions.def     |  1 +
 clang/include/clang/Basic/LangOptions.h       | 13 +++++-
 .../include/clang/Serialization/ASTBitCodes.h |  3 ++
 clang/include/clang/Serialization/ASTReader.h |  3 ++
 clang/lib/CodeGen/ModuleBuilder.cpp           |  9 ++++
 clang/lib/Serialization/ASTReader.cpp         | 10 +++++
 clang/lib/Serialization/ASTWriter.cpp         |  6 +++
 clang/test/CodeGenHLSL/precise-pch.hlsl       | 34 +++++++++++++++
 clang/test/CodeGenHLSL/precise.hlsl           | 41 ++++++++++++++-----
 clang/unittests/Basic/LangOptionsTest.cpp     | 32 +++++++++++++++
 10 files changed, 141 insertions(+), 11 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/precise-pch.hlsl

diff --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index 155d365771b836..71a59b2e4abc50 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -246,6 +246,7 @@ LANGOPT(OpenMPTargetAtomicReduction , 1, 0, NotCompatible, 
"Use atomic operation
 LANGOPT(NoGPULib  , 1, 0, NotCompatible, "Indicate a build without the 
standard GPU libraries.")
 
 LANGOPT(HLSL, 1, 0, NotCompatible, "HLSL")
+LANGOPT(HLSLPrecise, 1, 0, Benign, "TU-wide HLSL precise floating-point mode")
 ENUM_LANGOPT(HLSLVersion, HLSLLangStd, 16, HLSL_Unset, NotCompatible, "HLSL 
Version")
 LANGOPT(HLSLStrictAvailability, 1, 0, NotCompatible,
         "Strict availability diagnostic mode for HLSL built-in functions.")
diff --git a/clang/include/clang/Basic/LangOptions.h 
b/clang/include/clang/Basic/LangOptions.h
index 160c53fed49350..5253d29c18e383 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -1090,7 +1090,18 @@ class FPOptionsOverride {
   }
 
   FPOptions applyOverrides(const LangOptions &LO) {
-    return applyOverrides(FPOptions(LO));
+    FPOptions Result = applyOverrides(FPOptions(LO));
+    if (LO.HLSLPrecise) {
+      // TU-wide HLSL precise mode takes precedence over unsafe FP pragmas.
+      Result.setFPContractMode(LangOptions::FPM_Off);
+      Result.setAllowFPReassociate(false);
+      Result.setNoHonorNaNs(false);
+      Result.setNoHonorInfs(false);
+      Result.setNoSignedZero(false);
+      Result.setAllowReciprocal(false);
+      Result.setAllowApproxFunc(false);
+    }
+    return Result;
   }
 
   bool operator==(FPOptionsOverride other) const {
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index ae3a2fad6ebe7f..e05bd1d9191438 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -754,6 +754,9 @@ enum ASTRecordTypes {
 
   /// Record code for the OpenMP 'requires' directives seen in the TU.
   OMP_REQUIRES_DECLS = 81,
+
+  /// Whether the HLSL 'precise' keyword was used in the TU.
+  HLSL_PRECISE_USED = 82,
 };
 
 /// Record types used within a source manager block.
diff --git a/clang/include/clang/Serialization/ASTReader.h 
b/clang/include/clang/Serialization/ASTReader.h
index 275383d40cf808..d10fb6c64aed78 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -983,6 +983,9 @@ class ASTReader : public ExternalPreprocessorSource,
   /// The floating point pragma option settings.
   SmallVector<uint64_t, 1> FPPragmaOptions;
 
+  /// Whether any loaded AST file used the HLSL 'precise' keyword.
+  bool HLSLPreciseUsed = false;
+
   /// The pragma clang optimize location (if the pragma state is "off").
   SourceLocation OptimizeOffPragmaLocation;
 
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp 
b/clang/lib/CodeGen/ModuleBuilder.cpp
index a3bd38b7824099..8803ef910dff45 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -242,6 +242,15 @@ namespace {
         return;
       // ASTContext refers to the CompilerInvocation's mutable LangOptions.
       LangOptions &LO = const_cast<LangOptions &>(Ctx->getLangOpts());
+      LO.HLSLPrecise = true;
+      LO.FastMath = false;
+      LO.UnsafeFPMath = false;
+      LO.AllowFPReassoc = false;
+      LO.NoHonorNaNs = false;
+      LO.NoHonorInfs = false;
+      LO.NoSignedZero = false;
+      LO.AllowRecip = false;
+      LO.ApproxFunc = false;
       LO.setDefaultFPContractMode(LangOptions::FPM_Off);
       LO.setComplexRange(LangOptions::CX_Full);
       // Like CompilerInstance::createTarget, only enable the rounding and
diff --git a/clang/lib/Serialization/ASTReader.cpp 
b/clang/lib/Serialization/ASTReader.cpp
index e6f6be496e5e58..1784d6098f3b77 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -4503,6 +4503,13 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       FPPragmaOptions.swap(Record);
       break;
 
+    case HLSL_PRECISE_USED:
+      if (Record.size() != 1)
+        return llvm::createStringError(std::errc::illegal_byte_sequence,
+                                       "invalid HLSL precise record");
+      HLSLPreciseUsed |= Record[0] != 0;
+      break;
+
     case DECLS_WITH_EFFECTS_TO_VERIFY:
       for (unsigned I = 0, N = Record.size(); I != N; /*in loop*/)
         DeclsWithEffectsToVerify.push_back(ReadDeclID(F, Record, I));
@@ -5764,6 +5771,9 @@ void ASTReader::InitializeContext() {
   assert(ContextObj && "no context to initialize");
   ASTContext &Context = *ContextObj;
 
+  if (HLSLPreciseUsed)
+    Context.setHLSLPreciseUsed();
+
   // If there's a listener, notify them that we "read" the translation unit.
   if (DeserializationListener)
     DeserializationListener->DeclRead(
diff --git a/clang/lib/Serialization/ASTWriter.cpp 
b/clang/lib/Serialization/ASTWriter.cpp
index 0afba1c84fd627..bd993f72e84be4 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -954,6 +954,7 @@ void ASTWriter::WriteBlockInfoBlock() {
   RECORD(CUDA_SPECIAL_DECL_REFS);
   RECORD(HEADER_SEARCH_TABLE);
   RECORD(FP_PRAGMA_OPTIONS);
+  RECORD(HLSL_PRECISE_USED);
   RECORD(OPENCL_EXTENSIONS);
   RECORD(OPENCL_EXTENSION_TYPES);
   RECORD(OPENCL_EXTENSION_DECLS);
@@ -5935,6 +5936,11 @@ void ASTWriter::WriteSpecialDeclRecords(Sema &SemaRef) {
 
   bool isModule = WritingModule != nullptr;
 
+  if (getLangOpts().HLSL) {
+    RecordData HLSLPreciseUsed = {Context.isHLSLPreciseUsed()};
+    Stream.EmitRecord(HLSL_PRECISE_USED, HLSLPreciseUsed);
+  }
+
   // Write the record containing external, unnamed definitions.
   if (!EagerlyDeserializedDecls.empty())
     Stream.EmitRecord(EAGERLY_DESERIALIZED_DECLS, EagerlyDeserializedDecls);
diff --git a/clang/test/CodeGenHLSL/precise-pch.hlsl 
b/clang/test/CodeGenHLSL/precise-pch.hlsl
new file mode 100644
index 00000000000000..b64cb4e5a8b1c2
--- /dev/null
+++ b/clang/test/CodeGenHLSL/precise-pch.hlsl
@@ -0,0 +1,34 @@
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-emit-pch -o %t/header.pch %t/header.hlsl
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-include-pch %t/header.pch -emit-llvm -disable-llvm-passes -o - %t/source.hlsl 
| FileCheck %s --check-prefixes=CHECK,PRECISE --implicit-check-not=nofpclass
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-include-pch %t/header.pch -emit-pch -o %t/chain.pch %t/chain.hlsl
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-include-pch %t/chain.pch -emit-llvm -disable-llvm-passes -o - %t/source.hlsl | 
FileCheck %s --check-prefixes=CHECK,PRECISE --implicit-check-not=nofpclass
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -ffp-contract=on 
-emit-pch -o %t/spirv.pch %t/header.hlsl
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -ffp-contract=on 
-include-pch %t/spirv.pch -emit-llvm -disable-llvm-passes -o - %t/source.hlsl | 
FileCheck %s --check-prefixes=CHECK,PRECISE --implicit-check-not=nofpclass
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-DNO_PRECISE -emit-pch -o %t/default.pch %t/header.hlsl
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-DNO_PRECISE -include-pch %t/default.pch -emit-llvm -disable-llvm-passes -o - 
%t/source.hlsl | FileCheck %s --check-prefixes=CHECK,DEFAULT
+
+// CHECK-LABEL: define {{.*}}float @_Z10FromHeaderff(
+// PRECISE: %[[MUL:.*]] = fmul float
+// PRECISE: fadd float %[[MUL]],
+// DEFAULT: call {{.*}}float @llvm.fmuladd.f32(
+// CHECK-LABEL: define {{.*}}float @_Z10FromSourceff(
+// PRECISE: %[[MUL:.*]] = fmul float
+// PRECISE: fadd float %[[MUL]],
+// DEFAULT: call {{.*}}float @llvm.fmuladd.f32(
+
+//--- header.hlsl
+#ifndef NO_PRECISE
+// Precise mode must be restored even if this field is never deserialized.
+struct Unused {
+  precise float X;
+};
+#endif
+
+export float FromHeader(float A, float B) { return A * B + A; }
+
+//--- chain.hlsl
+float Declaration(float A);
+
+//--- source.hlsl
+export float FromSource(float A, float B) { return A * B + A; }
diff --git a/clang/test/CodeGenHLSL/precise.hlsl 
b/clang/test/CodeGenHLSL/precise.hlsl
index e43e2d179568bc..087b7e5ca3f06d 100644
--- a/clang/test/CodeGenHLSL/precise.hlsl
+++ b/clang/test/CodeGenHLSL/precise.hlsl
@@ -1,31 +1,52 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,PRECISE
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,PRECISE --implicit-check-not=nofpclass 
--implicit-check-not='"no-signed-zeros-fp-math"="true"'
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffp-contract=on 
-emit-llvm -disable-llvm-passes -o - %s -DNO_PRECISE | FileCheck %s 
--check-prefixes=CHECK,DEFAULT
-// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -ffp-contract=on 
-emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,PRECISE
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -ffp-contract=on 
-emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,PRECISE --implicit-check-not=nofpclass 
--implicit-check-not='"no-signed-zeros-fp-math"="true"'
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -ffast-math 
-ffp-contract=fast -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,PRECISE --implicit-check-not=nofpclass 
--implicit-check-not='"no-signed-zeros-fp-math"="true"'
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -O2 -emit-llvm -o - 
%s | FileCheck %s --check-prefix=OPT-PRECISE
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -O2 -emit-llvm -o - 
%s | FileCheck %s --check-prefix=OPT-PRECISE
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -O2 -emit-llvm -o - 
%s -DNO_PRECISE | FileCheck %s --check-prefix=OPT-DEFAULT
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -fno-fast-math 
-ffp-contract=off -DFP_PRAGMAS -emit-llvm -disable-llvm-passes -o - %s | 
FileCheck %s --check-prefixes=CHECK,PRECISE
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -fno-fast-math 
-ffp-contract=off -DFP_PRAGMAS -emit-llvm -disable-llvm-passes -o - %s | 
FileCheck %s --check-prefixes=CHECK,PRECISE
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -fno-fast-math 
-ffp-contract=off -DFP_PRAGMAS -DNO_PRECISE -emit-llvm -disable-llvm-passes -o 
- %s | FileCheck %s --check-prefixes=CHECK,DEFAULT
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -fno-fast-math 
-DFP_PRAGMAS -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=OPT-PRECISE
 
 // A simplified version of precise is implemented: any use of the keyword in a
 // translation unit puts the whole translation unit in IEEE-strict mode, which
-// disables floating-point contraction. This includes code emitted before the
-// keyword is seen.
+// disables floating-point contraction and unsafe optimizations. This includes
+// code emitted before the keyword is seen.
+
+#ifdef FP_PRAGMAS
+#pragma float_control(precise, off)
+#pragma clang fp contract(on)
+#endif
 
 // CHECK-LABEL: define {{.*}}float @_Z6Beforeff(
-// PRECISE: %[[MUL:.*]] = fmul {{.*}}float
-// PRECISE: fadd {{.*}}float %[[MUL]],
+// PRECISE: %[[MUL:.*]] = fmul float
+// PRECISE: fadd float %[[MUL]],
 // PRECISE-NOT: fmuladd
 // DEFAULT: call {{.*}}float @llvm.fmuladd.f32(
 export float Before(float A, float B) { return A * B + A; }
 
+// OPT-PRECISE-LABEL: define {{.*}}float @_Z11Reassociatef(
+// OPT-PRECISE: %[[ADD:.*]] = fadd float %{{.*}}, 1.000000e+00
+// OPT-PRECISE: %[[SUB:.*]] = fsub float %[[ADD]], %{{.*}}
+// OPT-PRECISE: ret float %[[SUB]]
+// OPT-DEFAULT-LABEL: define {{.*}}float @_Z11Reassociatef(
+// OPT-DEFAULT: ret float 1.000000e+00
+export float Reassociate(float A) { return (A + 1.0) - A; }
+
 float Two() { return 2.0; }
 // The dynamic initializer of a global is also emitted in strict mode.
 // CHECK-LABEL: define internal {{.*}}void @__cxx_global_var_init(
-// PRECISE: %[[MUL:.*]] = fmul {{.*}}float
-// PRECISE: fadd {{.*}}float %[[MUL]],
+// PRECISE: %[[MUL:.*]] = fmul float
+// PRECISE: fadd float %[[MUL]],
 // PRECISE-NOT: fmuladd
 // DEFAULT: call {{.*}}float @llvm.fmuladd.f32(
 static float G = Two() * Two() + Two();
 
 // CHECK-LABEL: define {{.*}}float @_Z5Afterf(
-// PRECISE: %[[MUL:.*]] = fmul {{.*}}float
-// PRECISE: fadd {{.*}}float %[[MUL]],
+// PRECISE: %[[MUL:.*]] = fmul float
+// PRECISE: fadd float %[[MUL]],
 // PRECISE-NOT: fmuladd
 // DEFAULT: call {{.*}}float @llvm.fmuladd.f32(
 export float After(float A) {
diff --git a/clang/unittests/Basic/LangOptionsTest.cpp 
b/clang/unittests/Basic/LangOptionsTest.cpp
index 341ec7ba51b6e2..0f756b8f594cc6 100644
--- a/clang/unittests/Basic/LangOptionsTest.cpp
+++ b/clang/unittests/Basic/LangOptionsTest.cpp
@@ -55,4 +55,36 @@ TEST(LangOptsTest, CppStdLang) {
 
   EXPECT_FALSE(opts.getCLangStd());
 }
+
+TEST(LangOptsTest, HLSLPreciseOverridesUnsafeFPOptions) {
+  LangOptions Opts;
+  Opts.HLSL = true;
+
+  FPOptionsOverride Overrides;
+  Overrides.setFPPreciseEnabled(false);
+  Overrides.setFPEvalMethodOverride(LangOptions::FEM_Double);
+
+  FPOptions Result = Overrides.applyOverrides(Opts);
+  EXPECT_TRUE(Result.allowFPContractAcrossStatement());
+  EXPECT_TRUE(Result.getAllowFPReassociate());
+  EXPECT_TRUE(Result.getNoHonorNaNs());
+  EXPECT_TRUE(Result.getNoHonorInfs());
+  EXPECT_TRUE(Result.getNoSignedZero());
+  EXPECT_TRUE(Result.getAllowReciprocal());
+  EXPECT_TRUE(Result.getAllowApproxFunc());
+  EXPECT_EQ(Result.getFPEvalMethod(), LangOptions::FEM_Double);
+  EXPECT_FALSE(Result.getMathErrno());
+
+  Opts.HLSLPrecise = true;
+  Result = Overrides.applyOverrides(Opts);
+  EXPECT_EQ(Result.getFPContractMode(), LangOptions::FPM_Off);
+  EXPECT_FALSE(Result.getAllowFPReassociate());
+  EXPECT_FALSE(Result.getNoHonorNaNs());
+  EXPECT_FALSE(Result.getNoHonorInfs());
+  EXPECT_FALSE(Result.getNoSignedZero());
+  EXPECT_FALSE(Result.getAllowReciprocal());
+  EXPECT_FALSE(Result.getAllowApproxFunc());
+  EXPECT_EQ(Result.getFPEvalMethod(), LangOptions::FEM_Double);
+  EXPECT_FALSE(Result.getMathErrno());
+}
 } // namespace

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to