Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -843,6 +843,8 @@ setFPContractModeOverride(LangOptions::FPM_Off); } + void setDisallowFenvAccess(bool Value) { setRoundingMathOverride(Value); } + void setFPPreciseEnabled(bool Value) { setAllowFPReassociateOverride(!Value); setNoHonorNaNsOverride(!Value); Index: clang/lib/Sema/SemaAttr.cpp =================================================================== --- clang/lib/Sema/SemaAttr.cpp +++ clang/lib/Sema/SemaAttr.cpp @@ -1336,6 +1336,8 @@ Diag(Loc, diag::err_pragma_fenv_requires_precise); } NewFPFeatures.setAllowFEnvAccessOverride(IsEnabled); + if (!IsEnabled) + NewFPFeatures.setDisallowFenvAccess(IsEnabled); FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); } Index: clang/test/CodeGen/pragma-fenv_access.c =================================================================== --- clang/test/CodeGen/pragma-fenv_access.c +++ clang/test/CodeGen/pragma-fenv_access.c @@ -224,3 +224,17 @@ // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") // DEFAULT: fadd float +#pragma STDC FENV_ACCESS ON +float func_19(float x, float y) { + return x + y; +} +// CHECK-LABEL: @func_19 +// STRICT: call float @llvm.experimental.constrained.fadd.f32(float %0, float %1, metadata !"round.dynamic", metadata !"fpexcept.strict") + +#pragma STDC FENV_ACCESS OFF +float func_20(float x, float y) { + return x + y; +} +// CHECK-LABEL: @func_20 +// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") +// DEFAULT: fadd float Index: fenv_04_06.patch =================================================================== --- /dev/null +++ fenv_04_06.patch @@ -0,0 +1,2653 @@ +commit 6cc529b6a3cf6a3d1f16adbcafdad4fd8631b8ed +Author: Zahira Ammarguellat +Date: Thu Apr 6 15:21:14 2023 -0400 + + Set 'rounding_mode' to 'tonearest' with '#pragma STDC FENV_ACCESS OFF'. + + In strict mode the 'roundin_mode' is set to 'dynamic'. Using this pragma to + get out of strict mode doesn't have any effect on the 'rounding_mode'. + See https://godbolt.org/z/zoGTf4j1G + This patch fixes that. + +diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h +index b8c223b28f8a..be32dd92975f 100644 +--- a/clang/include/clang/Basic/LangOptions.h ++++ b/clang/include/clang/Basic/LangOptions.h +@@ -1,936 +1,940 @@ + //===- LangOptions.h - C Language Family Language Options -------*- C++ -*-===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + // See https://llvm.org/LICENSE.txt for license information. + // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + // + //===----------------------------------------------------------------------===// + // + /// \file + /// Defines the clang::LangOptions interface. + // + //===----------------------------------------------------------------------===// + + #ifndef LLVM_CLANG_BASIC_LANGOPTIONS_H + #define LLVM_CLANG_BASIC_LANGOPTIONS_H + + #include "clang/Basic/CommentOptions.h" + #include "clang/Basic/LLVM.h" + #include "clang/Basic/LangStandard.h" + #include "clang/Basic/ObjCRuntime.h" + #include "clang/Basic/Sanitizers.h" + #include "clang/Basic/TargetCXXABI.h" + #include "clang/Basic/Visibility.h" + #include "llvm/ADT/FloatingPointMode.h" + #include "llvm/ADT/StringRef.h" + #include "llvm/TargetParser/Triple.h" + #include + #include + #include + + namespace clang { + + /// Bitfields of LangOptions, split out from LangOptions in order to ensure that + /// this large collection of bitfields is a trivial class type. + class LangOptionsBase { + friend class CompilerInvocation; + + public: + // Define simple language options (with no accessors). + #define LANGOPT(Name, Bits, Default, Description) unsigned Name : Bits; + #define ENUM_LANGOPT(Name, Type, Bits, Default, Description) + #include "clang/Basic/LangOptions.def" + + protected: + // Define language options of enumeration type. These are private, and will + // have accessors (below). + #define LANGOPT(Name, Bits, Default, Description) + #define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ + unsigned Name : Bits; + #include "clang/Basic/LangOptions.def" + }; + + /// In the Microsoft ABI, this controls the placement of virtual displacement + /// members used to implement virtual inheritance. + enum class MSVtorDispMode { Never, ForVBaseOverride, ForVFTable }; + + /// Shader programs run in specific pipeline stages. + /// The order of these values matters, and must be kept in sync with the + /// Triple Environment enum in llvm::Triple. The ordering is enforced in + /// static_asserts in Triple.cpp and in clang/Basic/HLSLRuntime.h. + enum class ShaderStage { + Pixel = 0, + Vertex, + Geometry, + Hull, + Domain, + Compute, + Library, + RayGeneration, + Intersection, + AnyHit, + ClosestHit, + Miss, + Callable, + Mesh, + Amplification, + Invalid, + }; + + /// Keeps track of the various options that can be + /// enabled, which controls the dialect of C or C++ that is accepted. + class LangOptions : public LangOptionsBase { + public: + using Visibility = clang::Visibility; + using RoundingMode = llvm::RoundingMode; + + enum GCMode { NonGC, GCOnly, HybridGC }; + enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq }; + + // Automatic variables live on the stack, and when trivial they're usually + // uninitialized because it's undefined behavior to use them without + // initializing them. + enum class TrivialAutoVarInitKind { Uninitialized, Zero, Pattern }; + + enum SignedOverflowBehaviorTy { + // Default C standard behavior. + SOB_Undefined, + + // -fwrapv + SOB_Defined, + + // -ftrapv + SOB_Trapping + }; + + // FIXME: Unify with TUKind. + enum CompilingModuleKind { + /// Not compiling a module interface at all. + CMK_None, + + /// Compiling a module from a module map. + CMK_ModuleMap, + + /// Compiling a module header unit. + CMK_HeaderUnit, + + /// Compiling a C++ modules interface unit. + CMK_ModuleInterface, + }; + + enum PragmaMSPointersToMembersKind { + PPTMK_BestCase, + PPTMK_FullGeneralitySingleInheritance, + PPTMK_FullGeneralityMultipleInheritance, + PPTMK_FullGeneralityVirtualInheritance + }; + + using MSVtorDispMode = clang::MSVtorDispMode; + + enum DefaultCallingConvention { + DCC_None, + DCC_CDecl, + DCC_FastCall, + DCC_StdCall, + DCC_VectorCall, + DCC_RegCall + }; + + enum AddrSpaceMapMangling { ASMM_Target, ASMM_On, ASMM_Off }; + + // Corresponds to _MSC_VER + enum MSVCMajorVersion { + MSVC2010 = 1600, + MSVC2012 = 1700, + MSVC2013 = 1800, + MSVC2015 = 1900, + MSVC2017 = 1910, + MSVC2017_5 = 1912, + MSVC2017_7 = 1914, + MSVC2019 = 1920, + MSVC2019_5 = 1925, + MSVC2019_8 = 1928, + }; + + enum SYCLMajorVersion { + SYCL_None, + SYCL_2017, + SYCL_2020, + // The "default" SYCL version to be used when none is specified on the + // frontend command line. + SYCL_Default = SYCL_2020 + }; + + enum HLSLLangStd { + HLSL_Unset = 0, + HLSL_2015 = 2015, + HLSL_2016 = 2016, + HLSL_2017 = 2017, + HLSL_2018 = 2018, + HLSL_2021 = 2021, + HLSL_202x = 2029, + }; + + /// Clang versions with different platform ABI conformance. + enum class ClangABI { + /// Attempt to be ABI-compatible with code generated by Clang 3.8.x + /// (SVN r257626). This causes <1 x long long> to be passed in an + /// integer register instead of an SSE register on x64_64. + Ver3_8, + + /// Attempt to be ABI-compatible with code generated by Clang 4.0.x + /// (SVN r291814). This causes move operations to be ignored when + /// determining whether a class type can be passed or returned directly. + Ver4, + + /// Attempt to be ABI-compatible with code generated by Clang 6.0.x + /// (SVN r321711). This causes determination of whether a type is + /// standard-layout to ignore collisions between empty base classes + /// and between base classes and member subobjects, which affects + /// whether we reuse base class tail padding in some ABIs. + Ver6, + + /// Attempt to be ABI-compatible with code generated by Clang 7.0.x + /// (SVN r338536). This causes alignof (C++) and _Alignof (C11) to be + /// compatible with __alignof (i.e., return the preferred alignment) + /// rather than returning the required alignment. + Ver7, + + /// Attempt to be ABI-compatible with code generated by Clang 9.0.x + /// (SVN r351319). This causes vectors of __int128 to be passed in memory + /// instead of passing in multiple scalar registers on x86_64 on Linux and + /// NetBSD. + Ver9, + + /// Attempt to be ABI-compatible with code generated by Clang 11.0.x + /// (git 2e10b7a39b93). This causes clang to pass unions with a 256-bit + /// vector member on the stack instead of using registers, to not properly + /// mangle substitutions for template names in some cases, and to mangle + /// declaration template arguments without a cast to the parameter type + /// even when that can lead to mangling collisions. + Ver11, + + /// Attempt to be ABI-compatible with code generated by Clang 12.0.x + /// (git 8e464dd76bef). This causes clang to mangle lambdas within + /// global-scope inline variables incorrectly. + Ver12, + + /// Attempt to be ABI-compatible with code generated by Clang 14.0.x. + /// This causes clang to: + /// - mangle dependent nested names incorrectly. + /// - make trivial only those defaulted copy constructors with a + /// parameter-type-list equivalent to the parameter-type-list of an + /// implicit declaration. + Ver14, + + /// Attempt to be ABI-compatible with code generated by Clang 15.0.x. + /// This causes clang to: + /// - Reverse the implementation for DR692, DR1395 and DR1432. + /// - pack non-POD members of packed structs. + /// - consider classes with defaulted special member functions non-pod. + Ver15, + + /// Conform to the underlying platform's C and C++ ABIs as closely + /// as we can. + Latest + }; + + enum class CoreFoundationABI { + /// No interoperability ABI has been specified + Unspecified, + /// CoreFoundation does not have any language interoperability + Standalone, + /// Interoperability with the ObjectiveC runtime + ObjectiveC, + /// Interoperability with the latest known version of the Swift runtime + Swift, + /// Interoperability with the Swift 5.0 runtime + Swift5_0, + /// Interoperability with the Swift 4.2 runtime + Swift4_2, + /// Interoperability with the Swift 4.1 runtime + Swift4_1, + }; + + enum FPModeKind { + // Disable the floating point pragma + FPM_Off, + + // Enable the floating point pragma + FPM_On, + + // Aggressively fuse FP ops (E.g. FMA) disregarding pragmas. + FPM_Fast, + + // Aggressively fuse FP ops and honor pragmas. + FPM_FastHonorPragmas + }; + + /// Possible floating point exception behavior. + enum FPExceptionModeKind { + /// Assume that floating-point exceptions are masked. + FPE_Ignore, + /// Transformations do not cause new exceptions but may hide some. + FPE_MayTrap, + /// Strictly preserve the floating-point exception semantics. + FPE_Strict, + /// Used internally to represent initial unspecified value. + FPE_Default + }; + + /// Possible float expression evaluation method choices. + enum FPEvalMethodKind { + /// The evaluation method cannot be determined or is inconsistent for this + /// target. + FEM_Indeterminable = -1, + /// Use the declared type for fp arithmetic. + FEM_Source = 0, + /// Use the type double for fp arithmetic. + FEM_Double = 1, + /// Use extended type for fp arithmetic. + FEM_Extended = 2, + /// Used only for FE option processing; this is only used to indicate that + /// the user did not specify an explicit evaluation method on the command + /// line and so the target should be queried for its default evaluation + /// method instead. + FEM_UnsetOnCommandLine = 3 + }; + + enum ExcessPrecisionKind { FPP_Standard, FPP_Fast, FPP_None }; + + /// Possible exception handling behavior. + enum class ExceptionHandlingKind { None, SjLj, WinEH, DwarfCFI, Wasm }; + + enum class LaxVectorConversionKind { + /// Permit no implicit vector bitcasts. + None, + /// Permit vector bitcasts between integer vectors with different numbers + /// of elements but the same total bit-width. + Integer, + /// Permit vector bitcasts between all vectors with the same total + /// bit-width. + All, + }; + + enum class AltivecSrcCompatKind { + // All vector compares produce scalars except vector pixel and vector bool. + // The types vector pixel and vector bool return vector results. + Mixed, + // All vector compares produce vector results as in GCC. + GCC, + // All vector compares produce scalars as in XL. + XL, + // Default clang behaviour. + Default = Mixed, + }; + + enum class SignReturnAddressScopeKind { + /// No signing for any function. + None, + /// Sign the return address of functions that spill LR. + NonLeaf, + /// Sign the return address of all functions, + All + }; + + enum class SignReturnAddressKeyKind { + /// Return address signing uses APIA key. + AKey, + /// Return address signing uses APIB key. + BKey + }; + + enum class ThreadModelKind { + /// POSIX Threads. + POSIX, + /// Single Threaded Environment. + Single + }; + + enum class ExtendArgsKind { + /// Integer arguments are sign or zero extended to 32/64 bits + /// during default argument promotions. + ExtendTo32, + ExtendTo64 + }; + + enum class GPUDefaultStreamKind { + /// Legacy default stream + Legacy, + /// Per-thread default stream + PerThread, + }; + + enum class DefaultVisiblityExportMapping { + None, + /// map only explicit default visibilities to exported + Explicit, + /// map all default visibilities to exported + All, + }; + + enum class StrictFlexArraysLevelKind { + /// Any trailing array member is a FAM. + Default = 0, + /// Any trailing array member of undefined, 0, or 1 size is a FAM. + OneZeroOrIncomplete = 1, + /// Any trailing array member of undefined or 0 size is a FAM. + ZeroOrIncomplete = 2, + /// Any trailing array member of undefined size is a FAM. + IncompleteOnly = 3, + }; + + public: + /// The used language standard. + LangStandard::Kind LangStd; + + /// Set of enabled sanitizers. + SanitizerSet Sanitize; + /// Is at least one coverage instrumentation type enabled. + bool SanitizeCoverage = false; + + /// Paths to files specifying which objects + /// (files, functions, variables) should not be instrumented. + std::vector NoSanitizeFiles; + + /// Paths to the XRay "always instrument" files specifying which + /// objects (files, functions, variables) should be imbued with the XRay + /// "always instrument" attribute. + /// WARNING: This is a deprecated field and will go away in the future. + std::vector XRayAlwaysInstrumentFiles; + + /// Paths to the XRay "never instrument" files specifying which + /// objects (files, functions, variables) should be imbued with the XRay + /// "never instrument" attribute. + /// WARNING: This is a deprecated field and will go away in the future. + std::vector XRayNeverInstrumentFiles; + + /// Paths to the XRay attribute list files, specifying which objects + /// (files, functions, variables) should be imbued with the appropriate XRay + /// attribute(s). + std::vector XRayAttrListFiles; + + /// Paths to special case list files specifying which entities + /// (files, functions) should or should not be instrumented. + std::vector ProfileListFiles; + + clang::ObjCRuntime ObjCRuntime; + + CoreFoundationABI CFRuntime = CoreFoundationABI::Unspecified; + + std::string ObjCConstantStringClass; + + /// The name of the handler function to be called when -ftrapv is + /// specified. + /// + /// If none is specified, abort (GCC-compatible behaviour). + std::string OverflowHandler; + + /// The module currently being compiled as specified by -fmodule-name. + std::string ModuleName; + + /// The name of the current module, of which the main source file + /// is a part. If CompilingModule is set, we are compiling the interface + /// of this module, otherwise we are compiling an implementation file of + /// it. This starts as ModuleName in case -fmodule-name is provided and + /// changes during compilation to reflect the current module. + std::string CurrentModule; + + /// The names of any features to enable in module 'requires' decls + /// in addition to the hard-coded list in Module.cpp and the target features. + /// + /// This list is sorted. + std::vector ModuleFeatures; + + /// Options for parsing comments. + CommentOptions CommentOpts; + + /// A list of all -fno-builtin-* function names (e.g., memset). + std::vector NoBuiltinFuncs; + + /// A prefix map for __FILE__, __BASE_FILE__ and __builtin_FILE(). + std::map> MacroPrefixMap; + + /// Triples of the OpenMP targets that the host code codegen should + /// take into account in order to generate accurate offloading descriptors. + std::vector OMPTargetTriples; + + /// Name of the IR file that contains the result of the OpenMP target + /// host code generation. + std::string OMPHostIRFile; + + /// The user provided compilation unit ID, if non-empty. This is used to + /// externalize static variables which is needed to support accessing static + /// device variables in host code for single source offloading languages + /// like CUDA/HIP. + std::string CUID; + + /// C++ ABI to compile with, if specified by the frontend through -fc++-abi=. + /// This overrides the default ABI used by the target. + std::optional CXXABI; + + /// Indicates whether the front-end is explicitly told that the + /// input is a header file (i.e. -x c-header). + bool IsHeaderFile = false; + + /// The default stream kind used for HIP kernel launching. + GPUDefaultStreamKind GPUDefaultStream; + + /// The seed used by the randomize structure layout feature. + std::string RandstructSeed; + + /// Indicates whether the __FILE__ macro should use the target's + /// platform-specific file separator or whether it should use the build + /// environment's platform-specific file separator. + /// + /// The plaform-specific path separator is the backslash(\) for Windows and + /// forward slash (/) elsewhere. + bool UseTargetPathSeparator = false; + + LangOptions(); + + /// Set language defaults for the given input language and + /// language standard in the given LangOptions object. + /// + /// \param Opts - The LangOptions object to set up. + /// \param Lang - The input language. + /// \param T - The target triple. + /// \param Includes - If the language requires extra headers to be implicitly + /// included, they will be appended to this list. + /// \param LangStd - The input language standard. + static void + setLangDefaults(LangOptions &Opts, Language Lang, const llvm::Triple &T, + std::vector &Includes, + LangStandard::Kind LangStd = LangStandard::lang_unspecified); + + // Define accessors/mutators for language options of enumeration type. + #define LANGOPT(Name, Bits, Default, Description) + #define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ + Type get##Name() const { return static_cast(Name); } \ + void set##Name(Type Value) { Name = static_cast(Value); } + #include "clang/Basic/LangOptions.def" + + /// Are we compiling a module? + bool isCompilingModule() const { + return getCompilingModule() != CMK_None; + } + + /// Are we compiling a standard c++ module interface? + bool isCompilingModuleInterface() const { + return getCompilingModule() == CMK_ModuleInterface; + } + + /// Are we compiling a module implementation? + bool isCompilingModuleImplementation() const { + return !isCompilingModule() && !ModuleName.empty(); + } + + /// Do we need to track the owning module for a local declaration? + bool trackLocalOwningModule() const { + return isCompilingModule() || ModulesLocalVisibility; + } + + bool isSignedOverflowDefined() const { + return getSignedOverflowBehavior() == SOB_Defined; + } + + bool isSubscriptPointerArithmetic() const { + return ObjCRuntime.isSubscriptPointerArithmetic() && + !ObjCSubscriptingLegacyRuntime; + } + + bool isCompatibleWithMSVC(MSVCMajorVersion MajorVersion) const { + return MSCompatibilityVersion >= MajorVersion * 100000U; + } + + /// Reset all of the options that are not considered when building a + /// module. + void resetNonModularOptions(); + + /// Is this a libc/libm function that is no longer recognized as a + /// builtin because a -fno-builtin-* option has been specified? + bool isNoBuiltinFunc(StringRef Name) const; + + /// True if any ObjC types may have non-trivial lifetime qualifiers. + bool allowsNonTrivialObjCLifetimeQualifiers() const { + return ObjCAutoRefCount || ObjCWeak; + } + + bool assumeFunctionsAreConvergent() const { + return ConvergentFunctions; + } + + /// Return the OpenCL C or C++ version as a VersionTuple. + VersionTuple getOpenCLVersionTuple() const; + + /// Return the OpenCL version that kernel language is compatible with + unsigned getOpenCLCompatibleVersion() const; + + /// Return the OpenCL C or C++ for OpenCL language name and version + /// as a string. + std::string getOpenCLVersionString() const; + + /// Returns true if functions without prototypes or functions with an + /// identifier list (aka K&R C functions) are not allowed. + bool requiresStrictPrototypes() const { + return CPlusPlus || C2x || DisableKNRFunctions; + } + + /// Returns true if implicit function declarations are allowed in the current + /// language mode. + bool implicitFunctionsAllowed() const { + return !requiresStrictPrototypes() && !OpenCL; + } + + /// Returns true if implicit int is part of the language requirements. + bool isImplicitIntRequired() const { return !CPlusPlus && !C99; } + + /// Returns true if implicit int is supported at all. + bool isImplicitIntAllowed() const { return !CPlusPlus && !C2x; } + + /// Check if return address signing is enabled. + bool hasSignReturnAddress() const { + return getSignReturnAddressScope() != SignReturnAddressScopeKind::None; + } + + /// Check if return address signing uses AKey. + bool isSignReturnAddressWithAKey() const { + return getSignReturnAddressKey() == SignReturnAddressKeyKind::AKey; + } + + /// Check if leaf functions are also signed. + bool isSignReturnAddressScopeAll() const { + return getSignReturnAddressScope() == SignReturnAddressScopeKind::All; + } + + bool hasSjLjExceptions() const { + return getExceptionHandling() == ExceptionHandlingKind::SjLj; + } + + bool hasSEHExceptions() const { + return getExceptionHandling() == ExceptionHandlingKind::WinEH; + } + + bool hasDWARFExceptions() const { + return getExceptionHandling() == ExceptionHandlingKind::DwarfCFI; + } + + bool hasWasmExceptions() const { + return getExceptionHandling() == ExceptionHandlingKind::Wasm; + } + + bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; } + + bool hasDefaultVisibilityExportMapping() const { + return getDefaultVisibilityExportMapping() != + DefaultVisiblityExportMapping::None; + } + + bool isExplicitDefaultVisibilityExportMapping() const { + return getDefaultVisibilityExportMapping() == + DefaultVisiblityExportMapping::Explicit; + } + + bool isAllDefaultVisibilityExportMapping() const { + return getDefaultVisibilityExportMapping() == + DefaultVisiblityExportMapping::All; + } + + /// Remap path prefix according to -fmacro-prefix-path option. + void remapPathPrefix(SmallVectorImpl &Path) const; + + RoundingMode getDefaultRoundingMode() const { + return RoundingMath ? RoundingMode::Dynamic + : RoundingMode::NearestTiesToEven; + } + + FPExceptionModeKind getDefaultExceptionMode() const { + FPExceptionModeKind EM = getFPExceptionMode(); + if (EM == FPExceptionModeKind::FPE_Default) + return FPExceptionModeKind::FPE_Ignore; + return EM; + } + }; + + /// Floating point control options + class FPOptionsOverride; + class FPOptions { + public: + // We start by defining the layout. + using storage_type = uint32_t; + + using RoundingMode = llvm::RoundingMode; + + static constexpr unsigned StorageBitSize = 8 * sizeof(storage_type); + + // Define a fake option named "First" so that we have a PREVIOUS even for the + // real first option. + static constexpr storage_type FirstShift = 0, FirstWidth = 0; + #define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \ + static constexpr storage_type NAME##Shift = \ + PREVIOUS##Shift + PREVIOUS##Width; \ + static constexpr storage_type NAME##Width = WIDTH; \ + static constexpr storage_type NAME##Mask = ((1 << NAME##Width) - 1) \ + << NAME##Shift; + #include "clang/Basic/FPOptions.def" + + static constexpr storage_type TotalWidth = 0 + #define OPTION(NAME, TYPE, WIDTH, PREVIOUS) +WIDTH + #include "clang/Basic/FPOptions.def" + ; + static_assert(TotalWidth <= StorageBitSize, "Too short type for FPOptions"); + + private: + storage_type Value; + + FPOptionsOverride getChangesSlow(const FPOptions &Base) const; + + public: + FPOptions() : Value(0) { + setFPContractMode(LangOptions::FPM_Off); + setConstRoundingMode(RoundingMode::Dynamic); + setSpecifiedExceptionMode(LangOptions::FPE_Default); + } + explicit FPOptions(const LangOptions &LO) { + Value = 0; + // The language fp contract option FPM_FastHonorPragmas has the same effect + // as FPM_Fast in frontend. For simplicity, use FPM_Fast uniformly in + // frontend. + auto LangOptContractMode = LO.getDefaultFPContractMode(); + if (LangOptContractMode == LangOptions::FPM_FastHonorPragmas) + LangOptContractMode = LangOptions::FPM_Fast; + setFPContractMode(LangOptContractMode); + setRoundingMath(LO.RoundingMath); + setConstRoundingMode(LangOptions::RoundingMode::Dynamic); + setSpecifiedExceptionMode(LO.getFPExceptionMode()); + setAllowFPReassociate(LO.AllowFPReassoc); + setNoHonorNaNs(LO.NoHonorNaNs); + setNoHonorInfs(LO.NoHonorInfs); + setNoSignedZero(LO.NoSignedZero); + setAllowReciprocal(LO.AllowRecip); + setAllowApproxFunc(LO.ApproxFunc); + if (getFPContractMode() == LangOptions::FPM_On && + getRoundingMode() == llvm::RoundingMode::Dynamic && + getExceptionMode() == LangOptions::FPE_Strict) + // If the FP settings are set to the "strict" model, then + // FENV access is set to true. (ffp-model=strict) + setAllowFEnvAccess(true); + else + setAllowFEnvAccess(LangOptions::FPM_Off); + } + + bool allowFPContractWithinStatement() const { + return getFPContractMode() == LangOptions::FPM_On; + } + void setAllowFPContractWithinStatement() { + setFPContractMode(LangOptions::FPM_On); + } + + bool allowFPContractAcrossStatement() const { + return getFPContractMode() == LangOptions::FPM_Fast; + } + void setAllowFPContractAcrossStatement() { + setFPContractMode(LangOptions::FPM_Fast); + } + + bool isFPConstrained() const { + return getRoundingMode() != llvm::RoundingMode::NearestTiesToEven || + getExceptionMode() != LangOptions::FPE_Ignore || + getAllowFEnvAccess(); + } + + RoundingMode getRoundingMode() const { + RoundingMode RM = getConstRoundingMode(); + if (RM == RoundingMode::Dynamic) { + // C2x: 7.6.2p3 If the FE_DYNAMIC mode is specified and FENV_ACCESS is + // "off", the translator may assume that the default rounding mode is in + // effect. + if (!getAllowFEnvAccess() && !getRoundingMath()) + RM = RoundingMode::NearestTiesToEven; + } + return RM; + } + + LangOptions::FPExceptionModeKind getExceptionMode() const { + LangOptions::FPExceptionModeKind EM = getSpecifiedExceptionMode(); + if (EM == LangOptions::FPExceptionModeKind::FPE_Default) { + if (getAllowFEnvAccess()) + return LangOptions::FPExceptionModeKind::FPE_Strict; + else + return LangOptions::FPExceptionModeKind::FPE_Ignore; + } + return EM; + } + + bool operator==(FPOptions other) const { return Value == other.Value; } + + /// Return the default value of FPOptions that's used when trailing + /// storage isn't required. + static FPOptions defaultWithoutTrailingStorage(const LangOptions &LO); + + storage_type getAsOpaqueInt() const { return Value; } + static FPOptions getFromOpaqueInt(storage_type Value) { + FPOptions Opts; + Opts.Value = Value; + return Opts; + } + + /// Return difference with the given option set. + FPOptionsOverride getChangesFrom(const FPOptions &Base) const; + + // We can define most of the accessors automatically: + #define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \ + TYPE get##NAME() const { \ + return static_cast((Value & NAME##Mask) >> NAME##Shift); \ + } \ + void set##NAME(TYPE value) { \ + Value = (Value & ~NAME##Mask) | (storage_type(value) << NAME##Shift); \ + } + #include "clang/Basic/FPOptions.def" + LLVM_DUMP_METHOD void dump(); + }; + + /// Represents difference between two FPOptions values. + /// + /// The effect of language constructs changing the set of floating point options + /// is usually a change of some FP properties while leaving others intact. This + /// class describes such changes by keeping information about what FP options + /// are overridden. + /// + /// The integral set of FP options, described by the class FPOptions, may be + /// represented as a default FP option set, defined by language standard and + /// command line options, with the overrides introduced by pragmas. + /// + /// The is implemented as a value of the new FPOptions plus a mask showing which + /// fields are actually set in it. + class FPOptionsOverride { + FPOptions Options = FPOptions::getFromOpaqueInt(0); + FPOptions::storage_type OverrideMask = 0; + + public: + using RoundingMode = llvm::RoundingMode; + + /// The type suitable for storing values of FPOptionsOverride. Must be twice + /// as wide as bit size of FPOption. + using storage_type = uint64_t; + static_assert(sizeof(storage_type) >= 2 * sizeof(FPOptions::storage_type), + "Too short type for FPOptionsOverride"); + + /// Bit mask selecting bits of OverrideMask in serialized representation of + /// FPOptionsOverride. + static constexpr storage_type OverrideMaskBits = + (static_cast(1) << FPOptions::StorageBitSize) - 1; + + FPOptionsOverride() {} + FPOptionsOverride(const LangOptions &LO) + : Options(LO), OverrideMask(OverrideMaskBits) {} + FPOptionsOverride(FPOptions FPO) + : Options(FPO), OverrideMask(OverrideMaskBits) {} + FPOptionsOverride(FPOptions FPO, FPOptions::storage_type Mask) + : Options(FPO), OverrideMask(Mask) {} + + bool requiresTrailingStorage() const { return OverrideMask != 0; } + + void setAllowFPContractWithinStatement() { + setFPContractModeOverride(LangOptions::FPM_On); + } + + void setAllowFPContractAcrossStatement() { + setFPContractModeOverride(LangOptions::FPM_Fast); + } + + void setDisallowFPContract() { + setFPContractModeOverride(LangOptions::FPM_Off); + } + ++ void setDisallowFenvAccess(bool Value) { ++ setRoundingMathOverride(Value); ++ } ++ + void setFPPreciseEnabled(bool Value) { + setAllowFPReassociateOverride(!Value); + setNoHonorNaNsOverride(!Value); + setNoHonorInfsOverride(!Value); + setNoSignedZeroOverride(!Value); + setAllowReciprocalOverride(!Value); + setAllowApproxFuncOverride(!Value); + if (Value) + /* Precise mode implies fp_contract=on and disables ffast-math */ + setAllowFPContractWithinStatement(); + else + /* Precise mode disabled sets fp_contract=fast and enables ffast-math */ + setAllowFPContractAcrossStatement(); + } + + storage_type getAsOpaqueInt() const { + return (static_cast(Options.getAsOpaqueInt()) + << FPOptions::StorageBitSize) | + OverrideMask; + } + static FPOptionsOverride getFromOpaqueInt(storage_type I) { + FPOptionsOverride Opts; + Opts.OverrideMask = I & OverrideMaskBits; + Opts.Options = FPOptions::getFromOpaqueInt(I >> FPOptions::StorageBitSize); + return Opts; + } + + FPOptions applyOverrides(FPOptions Base) { + FPOptions Result = + FPOptions::getFromOpaqueInt((Base.getAsOpaqueInt() & ~OverrideMask) | + (Options.getAsOpaqueInt() & OverrideMask)); + return Result; + } + + FPOptions applyOverrides(const LangOptions &LO) { + return applyOverrides(FPOptions(LO)); + } + + bool operator==(FPOptionsOverride other) const { + return Options == other.Options && OverrideMask == other.OverrideMask; + } + bool operator!=(FPOptionsOverride other) const { return !(*this == other); } + + #define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \ + bool has##NAME##Override() const { \ + return OverrideMask & FPOptions::NAME##Mask; \ + } \ + TYPE get##NAME##Override() const { \ + assert(has##NAME##Override()); \ + return Options.get##NAME(); \ + } \ + void clear##NAME##Override() { \ + /* Clear the actual value so that we don't have spurious differences when \ + * testing equality. */ \ + Options.set##NAME(TYPE(0)); \ + OverrideMask &= ~FPOptions::NAME##Mask; \ + } \ + void set##NAME##Override(TYPE value) { \ + Options.set##NAME(value); \ + OverrideMask |= FPOptions::NAME##Mask; \ + } + #include "clang/Basic/FPOptions.def" + LLVM_DUMP_METHOD void dump(); + }; + + inline FPOptionsOverride FPOptions::getChangesFrom(const FPOptions &Base) const { + if (Value == Base.Value) + return FPOptionsOverride(); + return getChangesSlow(Base); + } + + /// Describes the kind of translation unit being processed. + enum TranslationUnitKind { + /// The translation unit is a complete translation unit. + TU_Complete, + + /// The translation unit is a prefix to a translation unit, and is + /// not complete. + TU_Prefix, + + /// The translation unit is a module. + TU_Module, + + /// The translation unit is a is a complete translation unit that we might + /// incrementally extend later. + TU_Incremental + }; + + } // namespace clang + + #endif // LLVM_CLANG_BASIC_LANGOPTIONS_H +diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp +index 0b76c041bcd7..0c35f395621a 100644 +--- a/clang/lib/Sema/SemaAttr.cpp ++++ b/clang/lib/Sema/SemaAttr.cpp +@@ -1,1445 +1,1447 @@ + //===--- SemaAttr.cpp - Semantic Analysis for Attributes ------------------===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + // See https://llvm.org/LICENSE.txt for license information. + // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + // + //===----------------------------------------------------------------------===// + // + // This file implements semantic analysis for non-trivial attributes and + // pragmas. + // + //===----------------------------------------------------------------------===// + + #include "clang/AST/ASTConsumer.h" + #include "clang/AST/Attr.h" + #include "clang/AST/Expr.h" + #include "clang/Basic/TargetInfo.h" + #include "clang/Lex/Preprocessor.h" + #include "clang/Sema/Lookup.h" + #include "clang/Sema/SemaInternal.h" + #include + using namespace clang; + + //===----------------------------------------------------------------------===// + // Pragma 'pack' and 'options align' + //===----------------------------------------------------------------------===// + + Sema::PragmaStackSentinelRAII::PragmaStackSentinelRAII(Sema &S, + StringRef SlotLabel, + bool ShouldAct) + : S(S), SlotLabel(SlotLabel), ShouldAct(ShouldAct) { + if (ShouldAct) { + S.VtorDispStack.SentinelAction(PSK_Push, SlotLabel); + S.DataSegStack.SentinelAction(PSK_Push, SlotLabel); + S.BSSSegStack.SentinelAction(PSK_Push, SlotLabel); + S.ConstSegStack.SentinelAction(PSK_Push, SlotLabel); + S.CodeSegStack.SentinelAction(PSK_Push, SlotLabel); + S.StrictGuardStackCheckStack.SentinelAction(PSK_Push, SlotLabel); + } + } + + Sema::PragmaStackSentinelRAII::~PragmaStackSentinelRAII() { + if (ShouldAct) { + S.VtorDispStack.SentinelAction(PSK_Pop, SlotLabel); + S.DataSegStack.SentinelAction(PSK_Pop, SlotLabel); + S.BSSSegStack.SentinelAction(PSK_Pop, SlotLabel); + S.ConstSegStack.SentinelAction(PSK_Pop, SlotLabel); + S.CodeSegStack.SentinelAction(PSK_Pop, SlotLabel); + S.StrictGuardStackCheckStack.SentinelAction(PSK_Pop, SlotLabel); + } + } + + void Sema::AddAlignmentAttributesForRecord(RecordDecl *RD) { + AlignPackInfo InfoVal = AlignPackStack.CurrentValue; + AlignPackInfo::Mode M = InfoVal.getAlignMode(); + bool IsPackSet = InfoVal.IsPackSet(); + bool IsXLPragma = getLangOpts().XLPragmaPack; + + // If we are not under mac68k/natural alignment mode and also there is no pack + // value, we don't need any attributes. + if (!IsPackSet && M != AlignPackInfo::Mac68k && M != AlignPackInfo::Natural) + return; + + if (M == AlignPackInfo::Mac68k && (IsXLPragma || InfoVal.IsAlignAttr())) { + RD->addAttr(AlignMac68kAttr::CreateImplicit(Context)); + } else if (IsPackSet) { + // Check to see if we need a max field alignment attribute. + RD->addAttr(MaxFieldAlignmentAttr::CreateImplicit( + Context, InfoVal.getPackNumber() * 8)); + } + + if (IsXLPragma && M == AlignPackInfo::Natural) + RD->addAttr(AlignNaturalAttr::CreateImplicit(Context)); + + if (AlignPackIncludeStack.empty()) + return; + // The #pragma align/pack affected a record in an included file, so Clang + // should warn when that pragma was written in a file that included the + // included file. + for (auto &AlignPackedInclude : llvm::reverse(AlignPackIncludeStack)) { + if (AlignPackedInclude.CurrentPragmaLocation != + AlignPackStack.CurrentPragmaLocation) + break; + if (AlignPackedInclude.HasNonDefaultValue) + AlignPackedInclude.ShouldWarnOnInclude = true; + } + } + + void Sema::AddMsStructLayoutForRecord(RecordDecl *RD) { + if (MSStructPragmaOn) + RD->addAttr(MSStructAttr::CreateImplicit(Context)); + + // FIXME: We should merge AddAlignmentAttributesForRecord with + // AddMsStructLayoutForRecord into AddPragmaAttributesForRecord, which takes + // all active pragmas and applies them as attributes to class definitions. + if (VtorDispStack.CurrentValue != getLangOpts().getVtorDispMode()) + RD->addAttr(MSVtorDispAttr::CreateImplicit( + Context, unsigned(VtorDispStack.CurrentValue))); + } + + template + static void addGslOwnerPointerAttributeIfNotExisting(ASTContext &Context, + CXXRecordDecl *Record) { + if (Record->hasAttr() || Record->hasAttr()) + return; + + for (Decl *Redecl : Record->redecls()) + Redecl->addAttr(Attribute::CreateImplicit(Context, /*DerefType=*/nullptr)); + } + + void Sema::inferGslPointerAttribute(NamedDecl *ND, + CXXRecordDecl *UnderlyingRecord) { + if (!UnderlyingRecord) + return; + + const auto *Parent = dyn_cast(ND->getDeclContext()); + if (!Parent) + return; + + static llvm::StringSet<> Containers{ + "array", + "basic_string", + "deque", + "forward_list", + "vector", + "list", + "map", + "multiset", + "multimap", + "priority_queue", + "queue", + "set", + "stack", + "unordered_set", + "unordered_map", + "unordered_multiset", + "unordered_multimap", + }; + + static llvm::StringSet<> Iterators{"iterator", "const_iterator", + "reverse_iterator", + "const_reverse_iterator"}; + + if (Parent->isInStdNamespace() && Iterators.count(ND->getName()) && + Containers.count(Parent->getName())) + addGslOwnerPointerAttributeIfNotExisting(Context, + UnderlyingRecord); + } + + void Sema::inferGslPointerAttribute(TypedefNameDecl *TD) { + + QualType Canonical = TD->getUnderlyingType().getCanonicalType(); + + CXXRecordDecl *RD = Canonical->getAsCXXRecordDecl(); + if (!RD) { + if (auto *TST = + dyn_cast(Canonical.getTypePtr())) { + + RD = dyn_cast_or_null( + TST->getTemplateName().getAsTemplateDecl()->getTemplatedDecl()); + } + } + + inferGslPointerAttribute(TD, RD); + } + + void Sema::inferGslOwnerPointerAttribute(CXXRecordDecl *Record) { + static llvm::StringSet<> StdOwners{ + "any", + "array", + "basic_regex", + "basic_string", + "deque", + "forward_list", + "vector", + "list", + "map", + "multiset", + "multimap", + "optional", + "priority_queue", + "queue", + "set", + "stack", + "unique_ptr", + "unordered_set", + "unordered_map", + "unordered_multiset", + "unordered_multimap", + "variant", + }; + static llvm::StringSet<> StdPointers{ + "basic_string_view", + "reference_wrapper", + "regex_iterator", + }; + + if (!Record->getIdentifier()) + return; + + // Handle classes that directly appear in std namespace. + if (Record->isInStdNamespace()) { + if (Record->hasAttr() || Record->hasAttr()) + return; + + if (StdOwners.count(Record->getName())) + addGslOwnerPointerAttributeIfNotExisting(Context, Record); + else if (StdPointers.count(Record->getName())) + addGslOwnerPointerAttributeIfNotExisting(Context, Record); + + return; + } + + // Handle nested classes that could be a gsl::Pointer. + inferGslPointerAttribute(Record, Record); + } + + void Sema::ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind, + SourceLocation PragmaLoc) { + PragmaMsStackAction Action = Sema::PSK_Reset; + AlignPackInfo::Mode ModeVal = AlignPackInfo::Native; + + switch (Kind) { + // For most of the platforms we support, native and natural are the same. + // With XL, native is the same as power, natural means something else. + case POAK_Native: + case POAK_Power: + Action = Sema::PSK_Push_Set; + break; + case POAK_Natural: + Action = Sema::PSK_Push_Set; + ModeVal = AlignPackInfo::Natural; + break; + + // Note that '#pragma options align=packed' is not equivalent to attribute + // packed, it has a different precedence relative to attribute aligned. + case POAK_Packed: + Action = Sema::PSK_Push_Set; + ModeVal = AlignPackInfo::Packed; + break; + + case POAK_Mac68k: + // Check if the target supports this. + if (!this->Context.getTargetInfo().hasAlignMac68kSupport()) { + Diag(PragmaLoc, diag::err_pragma_options_align_mac68k_target_unsupported); + return; + } + Action = Sema::PSK_Push_Set; + ModeVal = AlignPackInfo::Mac68k; + break; + case POAK_Reset: + // Reset just pops the top of the stack, or resets the current alignment to + // default. + Action = Sema::PSK_Pop; + if (AlignPackStack.Stack.empty()) { + if (AlignPackStack.CurrentValue.getAlignMode() != AlignPackInfo::Native || + AlignPackStack.CurrentValue.IsPackAttr()) { + Action = Sema::PSK_Reset; + } else { + Diag(PragmaLoc, diag::warn_pragma_options_align_reset_failed) + << "stack empty"; + return; + } + } + break; + } + + AlignPackInfo Info(ModeVal, getLangOpts().XLPragmaPack); + + AlignPackStack.Act(PragmaLoc, Action, StringRef(), Info); + } + + void Sema::ActOnPragmaClangSection(SourceLocation PragmaLoc, + PragmaClangSectionAction Action, + PragmaClangSectionKind SecKind, + StringRef SecName) { + PragmaClangSection *CSec; + int SectionFlags = ASTContext::PSF_Read; + switch (SecKind) { + case PragmaClangSectionKind::PCSK_BSS: + CSec = &PragmaClangBSSSection; + SectionFlags |= ASTContext::PSF_Write | ASTContext::PSF_ZeroInit; + break; + case PragmaClangSectionKind::PCSK_Data: + CSec = &PragmaClangDataSection; + SectionFlags |= ASTContext::PSF_Write; + break; + case PragmaClangSectionKind::PCSK_Rodata: + CSec = &PragmaClangRodataSection; + break; + case PragmaClangSectionKind::PCSK_Relro: + CSec = &PragmaClangRelroSection; + break; + case PragmaClangSectionKind::PCSK_Text: + CSec = &PragmaClangTextSection; + SectionFlags |= ASTContext::PSF_Execute; + break; + default: + llvm_unreachable("invalid clang section kind"); + } + + if (Action == PragmaClangSectionAction::PCSA_Clear) { + CSec->Valid = false; + return; + } + + if (llvm::Error E = isValidSectionSpecifier(SecName)) { + Diag(PragmaLoc, diag::err_pragma_section_invalid_for_target) + << toString(std::move(E)); + CSec->Valid = false; + return; + } + + if (UnifySection(SecName, SectionFlags, PragmaLoc)) + return; + + CSec->Valid = true; + CSec->SectionName = std::string(SecName); + CSec->PragmaLocation = PragmaLoc; + } + + void Sema::ActOnPragmaPack(SourceLocation PragmaLoc, PragmaMsStackAction Action, + StringRef SlotLabel, Expr *alignment) { + bool IsXLPragma = getLangOpts().XLPragmaPack; + // XL pragma pack does not support identifier syntax. + if (IsXLPragma && !SlotLabel.empty()) { + Diag(PragmaLoc, diag::err_pragma_pack_identifer_not_supported); + return; + } + + const AlignPackInfo CurVal = AlignPackStack.CurrentValue; + Expr *Alignment = static_cast(alignment); + + // If specified then alignment must be a "small" power of two. + unsigned AlignmentVal = 0; + AlignPackInfo::Mode ModeVal = CurVal.getAlignMode(); + + if (Alignment) { + std::optional Val; + Val = Alignment->getIntegerConstantExpr(Context); + + // pack(0) is like pack(), which just works out since that is what + // we use 0 for in PackAttr. + if (Alignment->isTypeDependent() || !Val || + !(*Val == 0 || Val->isPowerOf2()) || Val->getZExtValue() > 16) { + Diag(PragmaLoc, diag::warn_pragma_pack_invalid_alignment); + return; // Ignore + } + + if (IsXLPragma && *Val == 0) { + // pack(0) does not work out with XL. + Diag(PragmaLoc, diag::err_pragma_pack_invalid_alignment); + return; // Ignore + } + + AlignmentVal = (unsigned)Val->getZExtValue(); + } + + if (Action == Sema::PSK_Show) { + // Show the current alignment, making sure to show the right value + // for the default. + // FIXME: This should come from the target. + AlignmentVal = CurVal.IsPackSet() ? CurVal.getPackNumber() : 8; + if (ModeVal == AlignPackInfo::Mac68k && + (IsXLPragma || CurVal.IsAlignAttr())) + Diag(PragmaLoc, diag::warn_pragma_pack_show) << "mac68k"; + else + Diag(PragmaLoc, diag::warn_pragma_pack_show) << AlignmentVal; + } + + // MSDN, C/C++ Preprocessor Reference > Pragma Directives > pack: + // "#pragma pack(pop, identifier, n) is undefined" + if (Action & Sema::PSK_Pop) { + if (Alignment && !SlotLabel.empty()) + Diag(PragmaLoc, diag::warn_pragma_pack_pop_identifier_and_alignment); + if (AlignPackStack.Stack.empty()) { + assert(CurVal.getAlignMode() == AlignPackInfo::Native && + "Empty pack stack can only be at Native alignment mode."); + Diag(PragmaLoc, diag::warn_pragma_pop_failed) << "pack" << "stack empty"; + } + } + + AlignPackInfo Info(ModeVal, AlignmentVal, IsXLPragma); + + AlignPackStack.Act(PragmaLoc, Action, SlotLabel, Info); + } + + bool Sema::ConstantFoldAttrArgs(const AttributeCommonInfo &CI, + MutableArrayRef Args) { + llvm::SmallVector Notes; + for (unsigned Idx = 0; Idx < Args.size(); Idx++) { + Expr *&E = Args.begin()[Idx]; + assert(E && "error are handled before"); + if (E->isValueDependent() || E->isTypeDependent()) + continue; + + // FIXME: Use DefaultFunctionArrayLValueConversion() in place of the logic + // that adds implicit casts here. + if (E->getType()->isArrayType()) + E = ImpCastExprToType(E, Context.getPointerType(E->getType()), + clang::CK_ArrayToPointerDecay) + .get(); + if (E->getType()->isFunctionType()) + E = ImplicitCastExpr::Create(Context, + Context.getPointerType(E->getType()), + clang::CK_FunctionToPointerDecay, E, nullptr, + VK_PRValue, FPOptionsOverride()); + if (E->isLValue()) + E = ImplicitCastExpr::Create(Context, E->getType().getNonReferenceType(), + clang::CK_LValueToRValue, E, nullptr, + VK_PRValue, FPOptionsOverride()); + + Expr::EvalResult Eval; + Notes.clear(); + Eval.Diag = &Notes; + + bool Result = E->EvaluateAsConstantExpr(Eval, Context); + + /// Result means the expression can be folded to a constant. + /// Note.empty() means the expression is a valid constant expression in the + /// current language mode. + if (!Result || !Notes.empty()) { + Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type) + << CI << (Idx + 1) << AANT_ArgumentConstantExpr; + for (auto &Note : Notes) + Diag(Note.first, Note.second); + return false; + } + assert(Eval.Val.hasValue()); + E = ConstantExpr::Create(Context, E, Eval.Val); + } + + return true; + } + + void Sema::DiagnoseNonDefaultPragmaAlignPack(PragmaAlignPackDiagnoseKind Kind, + SourceLocation IncludeLoc) { + if (Kind == PragmaAlignPackDiagnoseKind::NonDefaultStateAtInclude) { + SourceLocation PrevLocation = AlignPackStack.CurrentPragmaLocation; + // Warn about non-default alignment at #includes (without redundant + // warnings for the same directive in nested includes). + // The warning is delayed until the end of the file to avoid warnings + // for files that don't have any records that are affected by the modified + // alignment. + bool HasNonDefaultValue = + AlignPackStack.hasValue() && + (AlignPackIncludeStack.empty() || + AlignPackIncludeStack.back().CurrentPragmaLocation != PrevLocation); + AlignPackIncludeStack.push_back( + {AlignPackStack.CurrentValue, + AlignPackStack.hasValue() ? PrevLocation : SourceLocation(), + HasNonDefaultValue, /*ShouldWarnOnInclude*/ false}); + return; + } + + assert(Kind == PragmaAlignPackDiagnoseKind::ChangedStateAtExit && + "invalid kind"); + AlignPackIncludeState PrevAlignPackState = + AlignPackIncludeStack.pop_back_val(); + // FIXME: AlignPackStack may contain both #pragma align and #pragma pack + // information, diagnostics below might not be accurate if we have mixed + // pragmas. + if (PrevAlignPackState.ShouldWarnOnInclude) { + // Emit the delayed non-default alignment at #include warning. + Diag(IncludeLoc, diag::warn_pragma_pack_non_default_at_include); + Diag(PrevAlignPackState.CurrentPragmaLocation, diag::note_pragma_pack_here); + } + // Warn about modified alignment after #includes. + if (PrevAlignPackState.CurrentValue != AlignPackStack.CurrentValue) { + Diag(IncludeLoc, diag::warn_pragma_pack_modified_after_include); + Diag(AlignPackStack.CurrentPragmaLocation, diag::note_pragma_pack_here); + } + } + + void Sema::DiagnoseUnterminatedPragmaAlignPack() { + if (AlignPackStack.Stack.empty()) + return; + bool IsInnermost = true; + + // FIXME: AlignPackStack may contain both #pragma align and #pragma pack + // information, diagnostics below might not be accurate if we have mixed + // pragmas. + for (const auto &StackSlot : llvm::reverse(AlignPackStack.Stack)) { + Diag(StackSlot.PragmaPushLocation, diag::warn_pragma_pack_no_pop_eof); + // The user might have already reset the alignment, so suggest replacing + // the reset with a pop. + if (IsInnermost && + AlignPackStack.CurrentValue == AlignPackStack.DefaultValue) { + auto DB = Diag(AlignPackStack.CurrentPragmaLocation, + diag::note_pragma_pack_pop_instead_reset); + SourceLocation FixItLoc = + Lexer::findLocationAfterToken(AlignPackStack.CurrentPragmaLocation, + tok::l_paren, SourceMgr, LangOpts, + /*SkipTrailing=*/false); + if (FixItLoc.isValid()) + DB << FixItHint::CreateInsertion(FixItLoc, "pop"); + } + IsInnermost = false; + } + } + + void Sema::ActOnPragmaMSStruct(PragmaMSStructKind Kind) { + MSStructPragmaOn = (Kind == PMSST_ON); + } + + void Sema::ActOnPragmaMSComment(SourceLocation CommentLoc, + PragmaMSCommentKind Kind, StringRef Arg) { + auto *PCD = PragmaCommentDecl::Create( + Context, Context.getTranslationUnitDecl(), CommentLoc, Kind, Arg); + Context.getTranslationUnitDecl()->addDecl(PCD); + Consumer.HandleTopLevelDecl(DeclGroupRef(PCD)); + } + + void Sema::ActOnPragmaDetectMismatch(SourceLocation Loc, StringRef Name, + StringRef Value) { + auto *PDMD = PragmaDetectMismatchDecl::Create( + Context, Context.getTranslationUnitDecl(), Loc, Name, Value); + Context.getTranslationUnitDecl()->addDecl(PDMD); + Consumer.HandleTopLevelDecl(DeclGroupRef(PDMD)); + } + + void Sema::ActOnPragmaFPEvalMethod(SourceLocation Loc, + LangOptions::FPEvalMethodKind Value) { + FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + switch (Value) { + default: + llvm_unreachable("invalid pragma eval_method kind"); + case LangOptions::FEM_Source: + NewFPFeatures.setFPEvalMethodOverride(LangOptions::FEM_Source); + break; + case LangOptions::FEM_Double: + NewFPFeatures.setFPEvalMethodOverride(LangOptions::FEM_Double); + break; + case LangOptions::FEM_Extended: + NewFPFeatures.setFPEvalMethodOverride(LangOptions::FEM_Extended); + break; + } + if (getLangOpts().ApproxFunc) + Diag(Loc, diag::err_setting_eval_method_used_in_unsafe_context) << 0 << 0; + if (getLangOpts().AllowFPReassoc) + Diag(Loc, diag::err_setting_eval_method_used_in_unsafe_context) << 0 << 1; + if (getLangOpts().AllowRecip) + Diag(Loc, diag::err_setting_eval_method_used_in_unsafe_context) << 0 << 2; + FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); + CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + PP.setCurrentFPEvalMethod(Loc, Value); + } + + void Sema::ActOnPragmaFloatControl(SourceLocation Loc, + PragmaMsStackAction Action, + PragmaFloatControlKind Value) { + FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + if ((Action == PSK_Push_Set || Action == PSK_Push || Action == PSK_Pop) && + !CurContext->getRedeclContext()->isFileContext()) { + // Push and pop can only occur at file or namespace scope, or within a + // language linkage declaration. + Diag(Loc, diag::err_pragma_fc_pp_scope); + return; + } + switch (Value) { + default: + llvm_unreachable("invalid pragma float_control kind"); + case PFC_Precise: + NewFPFeatures.setFPPreciseEnabled(true); + FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures); + break; + case PFC_NoPrecise: + if (CurFPFeatures.getExceptionMode() == LangOptions::FPE_Strict) + Diag(Loc, diag::err_pragma_fc_noprecise_requires_noexcept); + else if (CurFPFeatures.getAllowFEnvAccess()) + Diag(Loc, diag::err_pragma_fc_noprecise_requires_nofenv); + else + NewFPFeatures.setFPPreciseEnabled(false); + FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures); + break; + case PFC_Except: + if (!isPreciseFPEnabled()) + Diag(Loc, diag::err_pragma_fc_except_requires_precise); + else + NewFPFeatures.setSpecifiedExceptionModeOverride(LangOptions::FPE_Strict); + FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures); + break; + case PFC_NoExcept: + NewFPFeatures.setSpecifiedExceptionModeOverride(LangOptions::FPE_Ignore); + FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures); + break; + case PFC_Push: + FpPragmaStack.Act(Loc, Sema::PSK_Push_Set, StringRef(), NewFPFeatures); + break; + case PFC_Pop: + if (FpPragmaStack.Stack.empty()) { + Diag(Loc, diag::warn_pragma_pop_failed) << "float_control" + << "stack empty"; + return; + } + FpPragmaStack.Act(Loc, Action, StringRef(), NewFPFeatures); + NewFPFeatures = FpPragmaStack.CurrentValue; + break; + } + CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + } + + void Sema::ActOnPragmaMSPointersToMembers( + LangOptions::PragmaMSPointersToMembersKind RepresentationMethod, + SourceLocation PragmaLoc) { + MSPointerToMemberRepresentationMethod = RepresentationMethod; + ImplicitMSInheritanceAttrLoc = PragmaLoc; + } + + void Sema::ActOnPragmaMSVtorDisp(PragmaMsStackAction Action, + SourceLocation PragmaLoc, + MSVtorDispMode Mode) { + if (Action & PSK_Pop && VtorDispStack.Stack.empty()) + Diag(PragmaLoc, diag::warn_pragma_pop_failed) << "vtordisp" + << "stack empty"; + VtorDispStack.Act(PragmaLoc, Action, StringRef(), Mode); + } + + template <> + void Sema::PragmaStack::Act(SourceLocation PragmaLocation, + PragmaMsStackAction Action, + llvm::StringRef StackSlotLabel, + AlignPackInfo Value) { + if (Action == PSK_Reset) { + CurrentValue = DefaultValue; + CurrentPragmaLocation = PragmaLocation; + return; + } + if (Action & PSK_Push) + Stack.emplace_back(Slot(StackSlotLabel, CurrentValue, CurrentPragmaLocation, + PragmaLocation)); + else if (Action & PSK_Pop) { + if (!StackSlotLabel.empty()) { + // If we've got a label, try to find it and jump there. + auto I = llvm::find_if(llvm::reverse(Stack), [&](const Slot &x) { + return x.StackSlotLabel == StackSlotLabel; + }); + // We found the label, so pop from there. + if (I != Stack.rend()) { + CurrentValue = I->Value; + CurrentPragmaLocation = I->PragmaLocation; + Stack.erase(std::prev(I.base()), Stack.end()); + } + } else if (Value.IsXLStack() && Value.IsAlignAttr() && + CurrentValue.IsPackAttr()) { + // XL '#pragma align(reset)' would pop the stack until + // a current in effect pragma align is popped. + auto I = llvm::find_if(llvm::reverse(Stack), [&](const Slot &x) { + return x.Value.IsAlignAttr(); + }); + // If we found pragma align so pop from there. + if (I != Stack.rend()) { + Stack.erase(std::prev(I.base()), Stack.end()); + if (Stack.empty()) { + CurrentValue = DefaultValue; + CurrentPragmaLocation = PragmaLocation; + } else { + CurrentValue = Stack.back().Value; + CurrentPragmaLocation = Stack.back().PragmaLocation; + Stack.pop_back(); + } + } + } else if (!Stack.empty()) { + // xl '#pragma align' sets the baseline, and `#pragma pack` cannot pop + // over the baseline. + if (Value.IsXLStack() && Value.IsPackAttr() && CurrentValue.IsAlignAttr()) + return; + + // We don't have a label, just pop the last entry. + CurrentValue = Stack.back().Value; + CurrentPragmaLocation = Stack.back().PragmaLocation; + Stack.pop_back(); + } + } + if (Action & PSK_Set) { + CurrentValue = Value; + CurrentPragmaLocation = PragmaLocation; + } + } + + bool Sema::UnifySection(StringRef SectionName, int SectionFlags, + NamedDecl *Decl) { + SourceLocation PragmaLocation; + if (auto A = Decl->getAttr()) + if (A->isImplicit()) + PragmaLocation = A->getLocation(); + auto SectionIt = Context.SectionInfos.find(SectionName); + if (SectionIt == Context.SectionInfos.end()) { + Context.SectionInfos[SectionName] = + ASTContext::SectionInfo(Decl, PragmaLocation, SectionFlags); + return false; + } + // A pre-declared section takes precedence w/o diagnostic. + const auto &Section = SectionIt->second; + if (Section.SectionFlags == SectionFlags || + ((SectionFlags & ASTContext::PSF_Implicit) && + !(Section.SectionFlags & ASTContext::PSF_Implicit))) + return false; + Diag(Decl->getLocation(), diag::err_section_conflict) << Decl << Section; + if (Section.Decl) + Diag(Section.Decl->getLocation(), diag::note_declared_at) + << Section.Decl->getName(); + if (PragmaLocation.isValid()) + Diag(PragmaLocation, diag::note_pragma_entered_here); + if (Section.PragmaSectionLocation.isValid()) + Diag(Section.PragmaSectionLocation, diag::note_pragma_entered_here); + return true; + } + + bool Sema::UnifySection(StringRef SectionName, + int SectionFlags, + SourceLocation PragmaSectionLocation) { + auto SectionIt = Context.SectionInfos.find(SectionName); + if (SectionIt != Context.SectionInfos.end()) { + const auto &Section = SectionIt->second; + if (Section.SectionFlags == SectionFlags) + return false; + if (!(Section.SectionFlags & ASTContext::PSF_Implicit)) { + Diag(PragmaSectionLocation, diag::err_section_conflict) + << "this" << Section; + if (Section.Decl) + Diag(Section.Decl->getLocation(), diag::note_declared_at) + << Section.Decl->getName(); + if (Section.PragmaSectionLocation.isValid()) + Diag(Section.PragmaSectionLocation, diag::note_pragma_entered_here); + return true; + } + } + Context.SectionInfos[SectionName] = + ASTContext::SectionInfo(nullptr, PragmaSectionLocation, SectionFlags); + return false; + } + + /// Called on well formed \#pragma bss_seg(). + void Sema::ActOnPragmaMSSeg(SourceLocation PragmaLocation, + PragmaMsStackAction Action, + llvm::StringRef StackSlotLabel, + StringLiteral *SegmentName, + llvm::StringRef PragmaName) { + PragmaStack *Stack = + llvm::StringSwitch *>(PragmaName) + .Case("data_seg", &DataSegStack) + .Case("bss_seg", &BSSSegStack) + .Case("const_seg", &ConstSegStack) + .Case("code_seg", &CodeSegStack); + if (Action & PSK_Pop && Stack->Stack.empty()) + Diag(PragmaLocation, diag::warn_pragma_pop_failed) << PragmaName + << "stack empty"; + if (SegmentName) { + if (!checkSectionName(SegmentName->getBeginLoc(), SegmentName->getString())) + return; + + if (SegmentName->getString() == ".drectve" && + Context.getTargetInfo().getCXXABI().isMicrosoft()) + Diag(PragmaLocation, diag::warn_attribute_section_drectve) << PragmaName; + } + + Stack->Act(PragmaLocation, Action, StackSlotLabel, SegmentName); + } + + /// Called on well formed \#pragma strict_gs_check(). + void Sema::ActOnPragmaMSStrictGuardStackCheck(SourceLocation PragmaLocation, + PragmaMsStackAction Action, + bool Value) { + if (Action & PSK_Pop && StrictGuardStackCheckStack.Stack.empty()) + Diag(PragmaLocation, diag::warn_pragma_pop_failed) << "strict_gs_check" + << "stack empty"; + + StrictGuardStackCheckStack.Act(PragmaLocation, Action, StringRef(), Value); + } + + /// Called on well formed \#pragma bss_seg(). + void Sema::ActOnPragmaMSSection(SourceLocation PragmaLocation, + int SectionFlags, StringLiteral *SegmentName) { + UnifySection(SegmentName->getString(), SectionFlags, PragmaLocation); + } + + void Sema::ActOnPragmaMSInitSeg(SourceLocation PragmaLocation, + StringLiteral *SegmentName) { + // There's no stack to maintain, so we just have a current section. When we + // see the default section, reset our current section back to null so we stop + // tacking on unnecessary attributes. + CurInitSeg = SegmentName->getString() == ".CRT$XCU" ? nullptr : SegmentName; + CurInitSegLoc = PragmaLocation; + } + + void Sema::ActOnPragmaMSAllocText( + SourceLocation PragmaLocation, StringRef Section, + const SmallVector> + &Functions) { + if (!CurContext->getRedeclContext()->isFileContext()) { + Diag(PragmaLocation, diag::err_pragma_expected_file_scope) << "alloc_text"; + return; + } + + for (auto &Function : Functions) { + IdentifierInfo *II; + SourceLocation Loc; + std::tie(II, Loc) = Function; + + DeclarationName DN(II); + NamedDecl *ND = LookupSingleName(TUScope, DN, Loc, LookupOrdinaryName); + if (!ND) { + Diag(Loc, diag::err_undeclared_use) << II->getName(); + return; + } + + auto *FD = dyn_cast(ND->getCanonicalDecl()); + if (!FD) { + Diag(Loc, diag::err_pragma_alloc_text_not_function); + return; + } + + if (getLangOpts().CPlusPlus && !FD->isInExternCContext()) { + Diag(Loc, diag::err_pragma_alloc_text_c_linkage); + return; + } + + FunctionToSectionMap[II->getName()] = std::make_tuple(Section, Loc); + } + } + + void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope, + SourceLocation PragmaLoc) { + + IdentifierInfo *Name = IdTok.getIdentifierInfo(); + LookupResult Lookup(*this, Name, IdTok.getLocation(), LookupOrdinaryName); + LookupParsedName(Lookup, curScope, nullptr, true); + + if (Lookup.empty()) { + Diag(PragmaLoc, diag::warn_pragma_unused_undeclared_var) + << Name << SourceRange(IdTok.getLocation()); + return; + } + + VarDecl *VD = Lookup.getAsSingle(); + if (!VD) { + Diag(PragmaLoc, diag::warn_pragma_unused_expected_var_arg) + << Name << SourceRange(IdTok.getLocation()); + return; + } + + // Warn if this was used before being marked unused. + if (VD->isUsed()) + Diag(PragmaLoc, diag::warn_used_but_marked_unused) << Name; + + VD->addAttr(UnusedAttr::CreateImplicit(Context, IdTok.getLocation(), + AttributeCommonInfo::AS_Pragma, + UnusedAttr::GNU_unused)); + } + + void Sema::AddCFAuditedAttribute(Decl *D) { + IdentifierInfo *Ident; + SourceLocation Loc; + std::tie(Ident, Loc) = PP.getPragmaARCCFCodeAuditedInfo(); + if (!Loc.isValid()) return; + + // Don't add a redundant or conflicting attribute. + if (D->hasAttr() || + D->hasAttr()) + return; + + AttributeCommonInfo Info(Ident, SourceRange(Loc), + AttributeCommonInfo::AS_Pragma); + D->addAttr(CFAuditedTransferAttr::CreateImplicit(Context, Info)); + } + + namespace { + + std::optional + getParentAttrMatcherRule(attr::SubjectMatchRule Rule) { + using namespace attr; + switch (Rule) { + default: + return std::nullopt; + #define ATTR_MATCH_RULE(Value, Spelling, IsAbstract) + #define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, IsNegated) \ + case Value: \ + return Parent; + #include "clang/Basic/AttrSubMatchRulesList.inc" + } + } + + bool isNegatedAttrMatcherSubRule(attr::SubjectMatchRule Rule) { + using namespace attr; + switch (Rule) { + default: + return false; + #define ATTR_MATCH_RULE(Value, Spelling, IsAbstract) + #define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, IsNegated) \ + case Value: \ + return IsNegated; + #include "clang/Basic/AttrSubMatchRulesList.inc" + } + } + + CharSourceRange replacementRangeForListElement(const Sema &S, + SourceRange Range) { + // Make sure that the ',' is removed as well. + SourceLocation AfterCommaLoc = Lexer::findLocationAfterToken( + Range.getEnd(), tok::comma, S.getSourceManager(), S.getLangOpts(), + /*SkipTrailingWhitespaceAndNewLine=*/false); + if (AfterCommaLoc.isValid()) + return CharSourceRange::getCharRange(Range.getBegin(), AfterCommaLoc); + else + return CharSourceRange::getTokenRange(Range); + } + + std::string + attrMatcherRuleListToString(ArrayRef Rules) { + std::string Result; + llvm::raw_string_ostream OS(Result); + for (const auto &I : llvm::enumerate(Rules)) { + if (I.index()) + OS << (I.index() == Rules.size() - 1 ? ", and " : ", "); + OS << "'" << attr::getSubjectMatchRuleSpelling(I.value()) << "'"; + } + return Result; + } + + } // end anonymous namespace + + void Sema::ActOnPragmaAttributeAttribute( + ParsedAttr &Attribute, SourceLocation PragmaLoc, + attr::ParsedSubjectMatchRuleSet Rules) { + Attribute.setIsPragmaClangAttribute(); + SmallVector SubjectMatchRules; + // Gather the subject match rules that are supported by the attribute. + SmallVector, 4> + StrictSubjectMatchRuleSet; + Attribute.getMatchRules(LangOpts, StrictSubjectMatchRuleSet); + + // Figure out which subject matching rules are valid. + if (StrictSubjectMatchRuleSet.empty()) { + // Check for contradicting match rules. Contradicting match rules are + // either: + // - a top-level rule and one of its sub-rules. E.g. variable and + // variable(is_parameter). + // - a sub-rule and a sibling that's negated. E.g. + // variable(is_thread_local) and variable(unless(is_parameter)) + llvm::SmallDenseMap, 2> + RulesToFirstSpecifiedNegatedSubRule; + for (const auto &Rule : Rules) { + attr::SubjectMatchRule MatchRule = attr::SubjectMatchRule(Rule.first); + std::optional ParentRule = + getParentAttrMatcherRule(MatchRule); + if (!ParentRule) + continue; + auto It = Rules.find(*ParentRule); + if (It != Rules.end()) { + // A sub-rule contradicts a parent rule. + Diag(Rule.second.getBegin(), + diag::err_pragma_attribute_matcher_subrule_contradicts_rule) + << attr::getSubjectMatchRuleSpelling(MatchRule) + << attr::getSubjectMatchRuleSpelling(*ParentRule) << It->second + << FixItHint::CreateRemoval( + replacementRangeForListElement(*this, Rule.second)); + // Keep going without removing this rule as it won't change the set of + // declarations that receive the attribute. + continue; + } + if (isNegatedAttrMatcherSubRule(MatchRule)) + RulesToFirstSpecifiedNegatedSubRule.insert( + std::make_pair(*ParentRule, Rule)); + } + bool IgnoreNegatedSubRules = false; + for (const auto &Rule : Rules) { + attr::SubjectMatchRule MatchRule = attr::SubjectMatchRule(Rule.first); + std::optional ParentRule = + getParentAttrMatcherRule(MatchRule); + if (!ParentRule) + continue; + auto It = RulesToFirstSpecifiedNegatedSubRule.find(*ParentRule); + if (It != RulesToFirstSpecifiedNegatedSubRule.end() && + It->second != Rule) { + // Negated sub-rule contradicts another sub-rule. + Diag( + It->second.second.getBegin(), + diag:: + err_pragma_attribute_matcher_negated_subrule_contradicts_subrule) + << attr::getSubjectMatchRuleSpelling( + attr::SubjectMatchRule(It->second.first)) + << attr::getSubjectMatchRuleSpelling(MatchRule) << Rule.second + << FixItHint::CreateRemoval( + replacementRangeForListElement(*this, It->second.second)); + // Keep going but ignore all of the negated sub-rules. + IgnoreNegatedSubRules = true; + RulesToFirstSpecifiedNegatedSubRule.erase(It); + } + } + + if (!IgnoreNegatedSubRules) { + for (const auto &Rule : Rules) + SubjectMatchRules.push_back(attr::SubjectMatchRule(Rule.first)); + } else { + for (const auto &Rule : Rules) { + if (!isNegatedAttrMatcherSubRule(attr::SubjectMatchRule(Rule.first))) + SubjectMatchRules.push_back(attr::SubjectMatchRule(Rule.first)); + } + } + Rules.clear(); + } else { + // Each rule in Rules must be a strict subset of the attribute's + // SubjectMatch rules. I.e. we're allowed to use + // `apply_to=variables(is_global)` on an attrubute with SubjectList<[Var]>, + // but should not allow `apply_to=variables` on an attribute which has + // `SubjectList<[GlobalVar]>`. + for (const auto &StrictRule : StrictSubjectMatchRuleSet) { + // First, check for exact match. + if (Rules.erase(StrictRule.first)) { + // Add the rule to the set of attribute receivers only if it's supported + // in the current language mode. + if (StrictRule.second) + SubjectMatchRules.push_back(StrictRule.first); + } + } + // Check remaining rules for subset matches. + auto RulesToCheck = Rules; + for (const auto &Rule : RulesToCheck) { + attr::SubjectMatchRule MatchRule = attr::SubjectMatchRule(Rule.first); + if (auto ParentRule = getParentAttrMatcherRule(MatchRule)) { + if (llvm::any_of(StrictSubjectMatchRuleSet, + [ParentRule](const auto &StrictRule) { + return StrictRule.first == *ParentRule && + StrictRule.second; // IsEnabled + })) { + SubjectMatchRules.push_back(MatchRule); + Rules.erase(MatchRule); + } + } + } + } + + if (!Rules.empty()) { + auto Diagnostic = + Diag(PragmaLoc, diag::err_pragma_attribute_invalid_matchers) + << Attribute; + SmallVector ExtraRules; + for (const auto &Rule : Rules) { + ExtraRules.push_back(attr::SubjectMatchRule(Rule.first)); + Diagnostic << FixItHint::CreateRemoval( + replacementRangeForListElement(*this, Rule.second)); + } + Diagnostic << attrMatcherRuleListToString(ExtraRules); + } + + if (PragmaAttributeStack.empty()) { + Diag(PragmaLoc, diag::err_pragma_attr_attr_no_push); + return; + } + + PragmaAttributeStack.back().Entries.push_back( + {PragmaLoc, &Attribute, std::move(SubjectMatchRules), /*IsUsed=*/false}); + } + + void Sema::ActOnPragmaAttributeEmptyPush(SourceLocation PragmaLoc, + const IdentifierInfo *Namespace) { + PragmaAttributeStack.emplace_back(); + PragmaAttributeStack.back().Loc = PragmaLoc; + PragmaAttributeStack.back().Namespace = Namespace; + } + + void Sema::ActOnPragmaAttributePop(SourceLocation PragmaLoc, + const IdentifierInfo *Namespace) { + if (PragmaAttributeStack.empty()) { + Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch) << 1; + return; + } + + // Dig back through the stack trying to find the most recently pushed group + // that in Namespace. Note that this works fine if no namespace is present, + // think of push/pops without namespaces as having an implicit "nullptr" + // namespace. + for (size_t Index = PragmaAttributeStack.size(); Index;) { + --Index; + if (PragmaAttributeStack[Index].Namespace == Namespace) { + for (const PragmaAttributeEntry &Entry : + PragmaAttributeStack[Index].Entries) { + if (!Entry.IsUsed) { + assert(Entry.Attribute && "Expected an attribute"); + Diag(Entry.Attribute->getLoc(), diag::warn_pragma_attribute_unused) + << *Entry.Attribute; + Diag(PragmaLoc, diag::note_pragma_attribute_region_ends_here); + } + } + PragmaAttributeStack.erase(PragmaAttributeStack.begin() + Index); + return; + } + } + + if (Namespace) + Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch) + << 0 << Namespace->getName(); + else + Diag(PragmaLoc, diag::err_pragma_attribute_stack_mismatch) << 1; + } + + void Sema::AddPragmaAttributes(Scope *S, Decl *D) { + if (PragmaAttributeStack.empty()) + return; + for (auto &Group : PragmaAttributeStack) { + for (auto &Entry : Group.Entries) { + ParsedAttr *Attribute = Entry.Attribute; + assert(Attribute && "Expected an attribute"); + assert(Attribute->isPragmaClangAttribute() && + "expected #pragma clang attribute"); + + // Ensure that the attribute can be applied to the given declaration. + bool Applies = false; + for (const auto &Rule : Entry.MatchRules) { + if (Attribute->appliesToDecl(D, Rule)) { + Applies = true; + break; + } + } + if (!Applies) + continue; + Entry.IsUsed = true; + PragmaAttributeCurrentTargetDecl = D; + ParsedAttributesView Attrs; + Attrs.addAtEnd(Attribute); + ProcessDeclAttributeList(S, D, Attrs); + PragmaAttributeCurrentTargetDecl = nullptr; + } + } + } + + void Sema::PrintPragmaAttributeInstantiationPoint() { + assert(PragmaAttributeCurrentTargetDecl && "Expected an active declaration"); + Diags.Report(PragmaAttributeCurrentTargetDecl->getBeginLoc(), + diag::note_pragma_attribute_applied_decl_here); + } + + void Sema::DiagnoseUnterminatedPragmaAttribute() { + if (PragmaAttributeStack.empty()) + return; + Diag(PragmaAttributeStack.back().Loc, diag::err_pragma_attribute_no_pop_eof); + } + + void Sema::ActOnPragmaOptimize(bool On, SourceLocation PragmaLoc) { + if(On) + OptimizeOffPragmaLocation = SourceLocation(); + else + OptimizeOffPragmaLocation = PragmaLoc; + } + + void Sema::ActOnPragmaMSOptimize(SourceLocation Loc, bool IsOn) { + if (!CurContext->getRedeclContext()->isFileContext()) { + Diag(Loc, diag::err_pragma_expected_file_scope) << "optimize"; + return; + } + + MSPragmaOptimizeIsOn = IsOn; + } + + void Sema::ActOnPragmaMSFunction( + SourceLocation Loc, const llvm::SmallVectorImpl &NoBuiltins) { + if (!CurContext->getRedeclContext()->isFileContext()) { + Diag(Loc, diag::err_pragma_expected_file_scope) << "function"; + return; + } + + MSFunctionNoBuiltins.insert(NoBuiltins.begin(), NoBuiltins.end()); + } + + void Sema::AddRangeBasedOptnone(FunctionDecl *FD) { + // In the future, check other pragmas if they're implemented (e.g. pragma + // optimize 0 will probably map to this functionality too). + if(OptimizeOffPragmaLocation.isValid()) + AddOptnoneAttributeIfNoConflicts(FD, OptimizeOffPragmaLocation); + } + + void Sema::AddSectionMSAllocText(FunctionDecl *FD) { + if (!FD->getIdentifier()) + return; + + StringRef Name = FD->getName(); + auto It = FunctionToSectionMap.find(Name); + if (It != FunctionToSectionMap.end()) { + StringRef Section; + SourceLocation Loc; + std::tie(Section, Loc) = It->second; + + if (!FD->hasAttr()) + FD->addAttr(SectionAttr::CreateImplicit(Context, Section)); + } + } + + void Sema::ModifyFnAttributesMSPragmaOptimize(FunctionDecl *FD) { + // Don't modify the function attributes if it's "on". "on" resets the + // optimizations to the ones listed on the command line + if (!MSPragmaOptimizeIsOn) + AddOptnoneAttributeIfNoConflicts(FD, FD->getBeginLoc()); + } + + void Sema::AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, + SourceLocation Loc) { + // Don't add a conflicting attribute. No diagnostic is needed. + if (FD->hasAttr() || FD->hasAttr()) + return; + + // Add attributes only if required. Optnone requires noinline as well, but if + // either is already present then don't bother adding them. + if (!FD->hasAttr()) + FD->addAttr(OptimizeNoneAttr::CreateImplicit(Context, Loc)); + if (!FD->hasAttr()) + FD->addAttr(NoInlineAttr::CreateImplicit(Context, Loc)); + } + + void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) { + SmallVector V(MSFunctionNoBuiltins.begin(), + MSFunctionNoBuiltins.end()); + if (!MSFunctionNoBuiltins.empty()) + FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size())); + } + + typedef std::vector > VisStack; + enum : unsigned { NoVisibility = ~0U }; + + void Sema::AddPushedVisibilityAttribute(Decl *D) { + if (!VisContext) + return; + + NamedDecl *ND = dyn_cast(D); + if (ND && ND->getExplicitVisibility(NamedDecl::VisibilityForValue)) + return; + + VisStack *Stack = static_cast(VisContext); + unsigned rawType = Stack->back().first; + if (rawType == NoVisibility) return; + + VisibilityAttr::VisibilityType type + = (VisibilityAttr::VisibilityType) rawType; + SourceLocation loc = Stack->back().second; + + D->addAttr(VisibilityAttr::CreateImplicit(Context, type, loc)); + } + + /// FreeVisContext - Deallocate and null out VisContext. + void Sema::FreeVisContext() { + delete static_cast(VisContext); + VisContext = nullptr; + } + + static void PushPragmaVisibility(Sema &S, unsigned type, SourceLocation loc) { + // Put visibility on stack. + if (!S.VisContext) + S.VisContext = new VisStack; + + VisStack *Stack = static_cast(S.VisContext); + Stack->push_back(std::make_pair(type, loc)); + } + + void Sema::ActOnPragmaVisibility(const IdentifierInfo* VisType, + SourceLocation PragmaLoc) { + if (VisType) { + // Compute visibility to use. + VisibilityAttr::VisibilityType T; + if (!VisibilityAttr::ConvertStrToVisibilityType(VisType->getName(), T)) { + Diag(PragmaLoc, diag::warn_attribute_unknown_visibility) << VisType; + return; + } + PushPragmaVisibility(*this, T, PragmaLoc); + } else { + PopPragmaVisibility(false, PragmaLoc); + } + } + + void Sema::ActOnPragmaFPContract(SourceLocation Loc, + LangOptions::FPModeKind FPC) { + FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + switch (FPC) { + case LangOptions::FPM_On: + NewFPFeatures.setAllowFPContractWithinStatement(); + break; + case LangOptions::FPM_Fast: + NewFPFeatures.setAllowFPContractAcrossStatement(); + break; + case LangOptions::FPM_Off: + NewFPFeatures.setDisallowFPContract(); + break; + case LangOptions::FPM_FastHonorPragmas: + llvm_unreachable("Should not happen"); + } + FpPragmaStack.Act(Loc, Sema::PSK_Set, StringRef(), NewFPFeatures); + CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + } + + void Sema::ActOnPragmaFPReassociate(SourceLocation Loc, bool IsEnabled) { + if (IsEnabled) { + // For value unsafe context, combining this pragma with eval method + // setting is not recommended. See comment in function FixupInvocation#506. + int Reason = -1; + if (getLangOpts().getFPEvalMethod() != LangOptions::FEM_UnsetOnCommandLine) + // Eval method set using the option 'ffp-eval-method'. + Reason = 1; + if (PP.getLastFPEvalPragmaLocation().isValid()) + // Eval method set using the '#pragma clang fp eval_method'. + // We could have both an option and a pragma used to the set the eval + // method. The pragma overrides the option in the command line. The Reason + // of the diagnostic is overriden too. + Reason = 0; + if (Reason != -1) + Diag(Loc, diag::err_setting_eval_method_used_in_unsafe_context) + << Reason << 4; + } + FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + NewFPFeatures.setAllowFPReassociateOverride(IsEnabled); + FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); + CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + } + + void Sema::ActOnPragmaFEnvRound(SourceLocation Loc, llvm::RoundingMode FPR) { + FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + NewFPFeatures.setConstRoundingModeOverride(FPR); + FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); + CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + } + + void Sema::setExceptionMode(SourceLocation Loc, + LangOptions::FPExceptionModeKind FPE) { + FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + NewFPFeatures.setSpecifiedExceptionModeOverride(FPE); + FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); + CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + } + + void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled) { + FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides(); + if (IsEnabled) { + // Verify Microsoft restriction: + // You can't enable fenv_access unless precise semantics are enabled. + // Precise semantics can be enabled either by the float_control + // pragma, or by using the /fp:precise or /fp:strict compiler options + if (!isPreciseFPEnabled()) + Diag(Loc, diag::err_pragma_fenv_requires_precise); + } + NewFPFeatures.setAllowFEnvAccessOverride(IsEnabled); ++ if (!IsEnabled) ++ NewFPFeatures.setDisallowFenvAccess(IsEnabled); + FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures); + CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts()); + } + + void Sema::ActOnPragmaFPExceptions(SourceLocation Loc, + LangOptions::FPExceptionModeKind FPE) { + setExceptionMode(Loc, FPE); + } + + void Sema::PushNamespaceVisibilityAttr(const VisibilityAttr *Attr, + SourceLocation Loc) { + // Visibility calculations will consider the namespace's visibility. + // Here we just want to note that we're in a visibility context + // which overrides any enclosing #pragma context, but doesn't itself + // contribute visibility. + PushPragmaVisibility(*this, NoVisibility, Loc); + } + + void Sema::PopPragmaVisibility(bool IsNamespaceEnd, SourceLocation EndLoc) { + if (!VisContext) { + Diag(EndLoc, diag::err_pragma_pop_visibility_mismatch); + return; + } + + // Pop visibility from stack + VisStack *Stack = static_cast(VisContext); + + const std::pair *Back = &Stack->back(); + bool StartsWithPragma = Back->first != NoVisibility; + if (StartsWithPragma && IsNamespaceEnd) { + Diag(Back->second, diag::err_pragma_push_visibility_mismatch); + Diag(EndLoc, diag::note_surrounding_namespace_ends_here); + + // For better error recovery, eat all pushes inside the namespace. + do { + Stack->pop_back(); + Back = &Stack->back(); + StartsWithPragma = Back->first != NoVisibility; + } while (StartsWithPragma); + } else if (!StartsWithPragma && !IsNamespaceEnd) { + Diag(EndLoc, diag::err_pragma_pop_visibility_mismatch); + Diag(Back->second, diag::note_surrounding_namespace_starts_here); + return; + } + + Stack->pop_back(); + // To simplify the implementation, never keep around an empty stack. + if (Stack->empty()) + FreeVisContext(); + } + + template + static bool checkCommonAttributeFeatures(Sema &S, const Ty *Node, + const ParsedAttr &A, + bool SkipArgCountCheck) { + // Several attributes carry different semantics than the parsing requires, so + // those are opted out of the common argument checks. + // + // We also bail on unknown and ignored attributes because those are handled + // as part of the target-specific handling logic. + if (A.getKind() == ParsedAttr::UnknownAttribute) + return false; + // Check whether the attribute requires specific language extensions to be + // enabled. + if (!A.diagnoseLangOpts(S)) + return true; + // Check whether the attribute appertains to the given subject. + if (!A.diagnoseAppertainsTo(S, Node)) + return true; + // Check whether the attribute is mutually exclusive with other attributes + // that have already been applied to the declaration. + if (!A.diagnoseMutualExclusion(S, Node)) + return true; + // Check whether the attribute exists in the target architecture. + if (S.CheckAttrTarget(A)) + return true; + + if (A.hasCustomParsing()) + return false; + + if (!SkipArgCountCheck) { + if (A.getMinArgs() == A.getMaxArgs()) { + // If there are no optional arguments, then checking for the argument + // count is trivial. + if (!A.checkExactlyNumArgs(S, A.getMinArgs())) + return true; + } else { + // There are optional arguments, so checking is slightly more involved. + if (A.getMinArgs() && !A.checkAtLeastNumArgs(S, A.getMinArgs())) + return true; + else if (!A.hasVariadicArg() && A.getMaxArgs() && + !A.checkAtMostNumArgs(S, A.getMaxArgs())) + return true; + } + } + + return false; + } + + bool Sema::checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A, + bool SkipArgCountCheck) { + return ::checkCommonAttributeFeatures(*this, D, A, SkipArgCountCheck); + } + bool Sema::checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A, + bool SkipArgCountCheck) { + return ::checkCommonAttributeFeatures(*this, S, A, SkipArgCountCheck); + } +diff --git a/clang/test/CodeGen/pragma-fenv_access.c b/clang/test/CodeGen/pragma-fenv_access.c +index c986d9cff2a1..806da25dcef8 100644 +--- a/clang/test/CodeGen/pragma-fenv_access.c ++++ b/clang/test/CodeGen/pragma-fenv_access.c +@@ -1,226 +1,240 @@ + // RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,STRICT %s + // RUN: %clang_cc1 -fexperimental-strict-floating-point -ffp-exception-behavior=strict -triple %itanium_abi_triple -emit-llvm %s -o - -fms-extensions -DMS | FileCheck --check-prefixes=CHECK,STRICT %s + // RUN: %clang_cc1 -fexperimental-strict-floating-point -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,DEFAULT %s + + + float func_00(float x, float y) { + return x + y; + } + // CHECK-LABEL: @func_00 + // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // DEFAULT: fadd float + + + #ifdef MS + #pragma fenv_access (on) + #else + #pragma STDC FENV_ACCESS ON + #endif + + float func_01(float x, float y) { + return x + y; + } + // CHECK-LABEL: @func_01 + // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + + float func_02(float x, float y) { + #pragma float_control(except, off) + #pragma STDC FENV_ACCESS OFF + return x + y; + } + // CHECK-LABEL: @func_02 + // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + + + float func_03(float x, float y) { + return x + y; + } + // CHECK-LABEL: @func_03 + // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + + #ifdef MS + #pragma fenv_access (off) + #else + #pragma STDC FENV_ACCESS OFF + #endif + + float func_04(float x, float y) { + #pragma float_control(except, off) + return x + y; + } + // CHECK-LABEL: @func_04 + // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + // DEFAULT: fadd float + + + float func_04a(float x, float y) { + #pragma float_control(except, on) + return x + y; + } + // CHECK-LABEL: @func_04a + // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + + + float func_05(float x, float y) { + #pragma STDC FENV_ACCESS ON + return x + y; + } + // CHECK-LABEL: @func_05 + // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + + float func_06(float x, float y) { + #pragma float_control(except, off) + return x + y; + } + // CHECK-LABEL: @func_06 + // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + // DEFAULT: fadd float + + + float func_07(float x, float y) { + x -= y; + if (x) { + #pragma STDC FENV_ACCESS ON + y *= 2.0F; + } + return y + 4.0F; + } + // CHECK-LABEL: @func_07 + // STRICT: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // STRICT: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // DEFAULT: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + // DEFAULT: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // DEFAULT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + + + float func_08(float x, float y) { + #pragma STDC FENV_ROUND FE_UPWARD + #pragma STDC FENV_ACCESS ON + return x + y; + } + // CHECK-LABEL: @func_08 + // CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.strict") + + + float func_09(float x, float y) { + #pragma STDC FENV_ROUND FE_TONEAREST + #pragma STDC FENV_ACCESS ON + return x + y; + } + // CHECK-LABEL: @func_09 + // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + + + float func_10(float x, float y) { + #pragma STDC FENV_ROUND FE_TONEAREST + #pragma clang fp exceptions(ignore) + #pragma STDC FENV_ACCESS ON + return x + y; + } + // CHECK-LABEL: @func_10 + // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + + + float func_11(float x, float y) { + #pragma STDC FENV_ROUND FE_TONEAREST + #pragma clang fp exceptions(ignore) + #pragma STDC FENV_ACCESS OFF + return x + y; + } + // CHECK-LABEL: @func_11 + // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + // DEFAULT: fadd float + + + float func_12(float x, float y) { + #pragma clang fp exceptions(maytrap) + #pragma STDC FENV_ACCESS ON + return x + y; + } + // CHECK-LABEL: @func_12 + // CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.maytrap") + + + float func_13(float x, float y) { + #pragma clang fp exceptions(maytrap) + #pragma STDC FENV_ROUND FE_UPWARD + #pragma STDC FENV_ACCESS ON + return x + y; + } + // CHECK-LABEL: @func_13 + // CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.maytrap") + + + float func_14(float x, float y, float z) { + #pragma STDC FENV_ACCESS ON + float res = x * y; + { + #pragma STDC FENV_ACCESS OFF + return res + z; + } + } + // CHECK-LABEL: @func_14 + // STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // DEFAULT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // DEFAULT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + + + float func_15(float x, float y, float z) { + #pragma STDC FENV_ROUND FE_TOWARDZERO + #pragma STDC FENV_ACCESS ON + float res = x * y; + { + #pragma STDC FENV_ACCESS OFF + return res + z; + } + } + // CHECK-LABEL: @func_15 + // STRICT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict") + // STRICT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict") + // DEFAULT: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict") + // DEFAULT: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") + + + float func_16(float x, float y) { + x -= y; + { + #pragma STDC FENV_ROUND FE_TONEAREST + #pragma STDC FENV_ACCESS ON + y *= 2.0F; + } + { + #pragma STDC FENV_ACCESS ON + return y + 4.0F; + } + } + // CHECK-LABEL: @func_16 + // STRICT: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // STRICT: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // DEFAULT: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") + // DEFAULT: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // DEFAULT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + + float func_17(float x, float y) { + #pragma STDC FENV_ROUND FE_DYNAMIC + #pragma STDC FENV_ACCESS ON + return x + y; + } + // CHECK-LABEL: @func_17 + // CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + + float func_18(float x, float y) { + #pragma STDC FENV_ROUND FE_DYNAMIC + return x + y; + } + // CHECK-LABEL: @func_18 + // STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // DEFAULT: fadd float + ++#pragma STDC FENV_ACCESS ON ++float func_19(float x, float y) { ++ return x + y; ++} ++// CHECK-LABEL: @func_19 ++// STRICT: call float @llvm.experimental.constrained.fadd.f32(float %0, float %1, metadata !"round.dynamic", metadata !"fpexcept.strict") ++ ++#pragma STDC FENV_ACCESS OFF ++float func_20(float x, float y) { ++ return x + y; ++} ++// CHECK-LABEL: @func_20 ++// STRICT: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") ++// DEFAULT: fadd float