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
