//===- 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/CFProtectionOptions.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/BinaryFormat/DXContainer.h"
#include "llvm/Support/AllocToken.h"
#include "llvm/TargetParser/Triple.h"
#include <optional>
#include <string>
#include <vector>

namespace clang {

/// 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,
};

enum class PointerAuthenticationMode : unsigned {
  None,
  Strip,
  SignAndStrip,
  SignAndAuth
};

/// 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;
  friend class CompilerInvocationBase;

public:
  using Visibility = clang::Visibility;
  using RoundingMode = llvm::RoundingMode;
  using CFBranchLabelSchemeKind = clang::CFBranchLabelSchemeKind;

  /// For ASTs produced with different option value, signifies their level of
  /// compatibility.
  enum class CompatibilityKind {
    /// Does affect the construction of the AST in a way that does prevent
    /// module interoperability.
    NotCompatible,
    /// Does affect the construction of the AST in a way that doesn't prevent
    /// interoperability (that is, the value can be different between an
    /// explicit module and the user of that module).
    Compatible,
    /// Does not affect the construction of the AST in any way (that is, the
    /// value can be different between an implicit module and the user of that
    /// module).
    Benign,
  };

  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,
    DCC_RtdCall
  };

  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,
    MSVC2022_3 = 1933,
    MSVC2022_9 = 1939,
  };

  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 = 2028,
    HLSL_202y = 2029,
  };

  /// Clang versions with different platform ABI conformance.
  enum class ClangABI {
#define ABI_VER_MAJOR_MINOR(Major, Minor) Ver##Major##_##Minor,
#define ABI_VER_MAJOR(Major) Ver##Major,
#define ABI_VER_LATEST(Latest) Latest
#include "clang/Basic/ABIVersions.def"
  };

  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 : unsigned {
    /// 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 class MatrixMemoryLayout : unsigned {
    // Use column-major layout for matrices
    MatrixColMajor = 0,
    // Use row-major layout for matrices
    MatrixRowMajor = 1,
  };

  enum ExcessPrecisionKind { FPP_Standard, FPP_Fast, FPP_None };

  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,
  };

  /// Exclude certain code patterns from being instrumented by arithmetic
  /// overflow sanitizers
  enum OverflowPatternExclusionKind {
    /// Don't exclude any overflow patterns from sanitizers
    None = 1 << 0,
    /// Exclude all overflow patterns (below)
    All = 1 << 1,
    /// if (a + b < a)
    AddSignedOverflowTest = 1 << 2,
    /// if (a + b < a)
    AddUnsignedOverflowTest = 1 << 3,
    /// -1UL
    NegUnsignedConst = 1 << 4,
    /// while (count--)
    PostDecrInWhile = 1 << 5,
  };

  enum class DefaultVisiblityExportMapping {
    None,
    /// map only explicit default visibilities to exported
    Explicit,
    /// map all default visibilities to exported
    All,
  };

  enum class VisibilityForcedKinds {
    /// Force hidden visibility
    ForceHidden,
    /// Force protected visibility
    ForceProtected,
    /// Force default visibility
    ForceDefault,
    /// Don't alter the visibility
    Source,
  };

  enum class VisibilityFromDLLStorageClassKinds {
    /// Keep the IR-gen assigned visibility.
    Keep,
    /// Override the IR-gen assigned visibility with default visibility.
    Default,
    /// Override the IR-gen assigned visibility with hidden visibility.
    Hidden,
    /// Override the IR-gen assigned visibility with protected visibility.
    Protected,
  };

  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,
  };

  /// Controls the various implementations for complex multiplication and
  // division.
  enum ComplexRangeKind {
    /// Implementation of complex division and multiplication using a call to
    ///  runtime library functions(generally the case, but the BE might
    /// sometimes replace the library call if it knows enough about the
    /// potential range of the inputs). Overflow and non-finite values are
    /// handled by the library implementation. This is the default value.
    CX_Full,

    /// Implementation of complex division offering an improved handling
    /// for overflow in intermediate calculations with no special handling for
    /// NaN and infinite values.
    CX_Improved,

    /// Implementation of complex division using algebraic formulas at
    /// higher precision. Overflow is handled. Non-finite values are handled in
    /// some cases. If the target hardware does not have native support for a
    /// higher precision data type, an implementation for the complex operation
    /// will be used to provide improved guards against intermediate overflow,
    /// but overflow and underflow may still occur in some cases. NaN and
    /// infinite values are not handled.
    CX_Promoted,

    /// Implementation of complex division and multiplication using
    /// algebraic formulas at source precision. No special handling to avoid
    /// overflow. NaN and infinite values are not handled.
    CX_Basic,

    /// No range rule is enabled.
    CX_None
  };

  /// Controls which variables have static destructors registered.
  enum class RegisterStaticDestructorsKind {
    /// Register static destructors for all variables.
    All,
    /// Register static destructors only for thread-local variables.
    ThreadLocal,
    /// Don't register static destructors for any variables.
    None,
  };

  enum class LayoutCompatibilityKind {
    /// Use default layout rules of the target.
    Default = 0,
    /// Use Itanium rules for bit-field layout and fundamental types alignment.
    Itanium = 1,
    /// Use Microsoft C++ ABI rules for bit-field layout and fundamental types
    /// alignment.
    Microsoft = 2,
  };

  // Define simple language options (with no accessors).
#define LANGOPT(Name, Bits, Default, Compatibility, Description)               \
  unsigned Name : Bits;
#define ENUM_LANGOPT(Name, Type, Bits, Default, Compatibility, 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, Compatibility, Description)
#define ENUM_LANGOPT(Name, Type, Bits, Default, Compatibility, Description)    \
  LLVM_PREFERRED_TYPE(Type)                                                    \
  unsigned Name : Bits;
#include "clang/Basic/LangOptions.def"
};

/// 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:
  /// 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<std::string> 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<std::string> 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<std::string> 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<std::string> XRayAttrListFiles;

  /// Paths to special case list files specifying which entities
  /// (files, functions) should or should not be instrumented.
  std::vector<std::string> 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<std::string> ModuleFeatures;

  /// Options for parsing comments.
  CommentOptions CommentOpts;

  /// A list of all -fno-builtin-* function names (e.g., memset).
  std::vector<std::string> NoBuiltinFuncs;

  /// A prefix map for __FILE__, __BASE_FILE__ and __builtin_FILE().
  std::map<std::string, std::string, std::greater<std::string>> MacroPrefixMap;

  /// Triples of the OpenMP targets that the host code codegen should
  /// take into account in order to generate accurate offloading descriptors.
  std::vector<llvm::Triple> 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<TargetCXXABI::Kind> 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;

  /// Which overflow patterns should be excluded from sanitizer instrumentation
  unsigned OverflowPatternExclusionMask = 0;

  std::vector<std::string> OverflowPatternExclusionValues;

  /// The seed used by the randomize structure layout feature.
  std::string RandstructSeed;

  /// Indicates whether to use target's platform-specific file separator when
  /// __FILE__ macro is used and when concatenating filename with directory or
  /// to use build environment environment's platform-specific file separator.
  ///
  /// The plaform-specific path separator is the backslash(\) for Windows and
  /// forward slash (/) elsewhere.
  bool UseTargetPathSeparator = false;

  // Indicates whether we should keep all nullptr checks for pointers
  // received as a result of a standard operator new (-fcheck-new)
  bool CheckNew = false;

  /// The HLSL root signature version for dxil.
  llvm::dxbc::RootSignatureVersion HLSLRootSigVer;

  /// The HLSL root signature that will be used to overide the root signature
  /// used for the shader entry point.
  std::string HLSLRootSigOverride;

  // Indicates if the wasm-opt binary must be ignored in the case of a
  // WebAssembly target.
  bool NoWasmOpt = false;

  /// Atomic code-generation options.
  /// These flags are set directly from the command-line options.
  bool AtomicRemoteMemory = false;
  bool AtomicFineGrainedMemory = false;
  bool AtomicIgnoreDenormalMode = false;

  /// Maximum number of allocation tokens (0 = target SIZE_MAX), nullopt if none
  /// set (use target SIZE_MAX).
  std::optional<uint64_t> AllocTokenMax;

  /// The allocation token mode.
  std::optional<llvm::AllocTokenMode> AllocTokenMode;

  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<std::string> &Includes,
                  LangStandard::Kind LangStd = LangStandard::lang_unspecified);

  // Define accessors/mutators for language options of enumeration type.
#define LANGOPT(Name, Bits, Default, Compatibility, Description)
#define ENUM_LANGOPT(Name, Type, Bits, Default, Compatibility, Description)    \
  Type get##Name() const { return static_cast<Type>(Name); }                   \
  void set##Name(Type Value) {                                                 \
    assert(static_cast<unsigned>(Value) < (1u << Bits));                       \
    Name = static_cast<unsigned>(Value);                                       \
  }
#include "clang/Basic/LangOptions.def"

  /// Are we compiling a module?
  bool isCompilingModule() const {
    return getCompilingModule() != CMK_None;
  }

  /// 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() const { return MSCompatibilityVersion > 0; }

  bool isCompatibleWithMSVC(MSVCMajorVersion MajorVersion) const {
    return MSCompatibilityVersion >= MajorVersion * 100000U;
  }

  bool isOverflowPatternExcluded(OverflowPatternExclusionKind Kind) const {
    if (OverflowPatternExclusionMask & OverflowPatternExclusionKind::None)
      return false;
    if (OverflowPatternExclusionMask & OverflowPatternExclusionKind::All)
      return true;
    return OverflowPatternExclusionMask & Kind;
  }

  /// 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 true if atomicrmw operations targeting allocations in private
  /// memory are undefined.
  bool threadPrivateMemoryAtomicsAreUndefined() const {
    // Should be false for OpenMP.
    // TODO: Should this be true for SYCL?
    return OpenCL || CUDA;
  }

  /// 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 || C23 || DisableKNRFunctions;
  }

  /// Returns true if implicit function declarations are allowed in the current
  /// language mode.
  bool implicitFunctionsAllowed() const {
    return !requiresStrictPrototypes() && !OpenCL;
  }

  /// Returns true if the language supports calling the 'atexit' function.
  bool hasAtExit() const { return !(OpenMP && OpenMPIsTargetDevice); }

  /// 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 && !C23; }

  /// 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 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;
  }

  bool hasGlobalAllocationFunctionVisibility() const {
    return getGlobalAllocationFunctionVisibility() !=
           VisibilityForcedKinds::Source;
  }

  bool hasDefaultGlobalAllocationFunctionVisibility() const {
    return getGlobalAllocationFunctionVisibility() ==
           VisibilityForcedKinds::ForceDefault;
  }

  bool hasProtectedGlobalAllocationFunctionVisibility() const {
    return getGlobalAllocationFunctionVisibility() ==
           VisibilityForcedKinds::ForceProtected;
  }

  bool hasHiddenGlobalAllocationFunctionVisibility() const {
    return getGlobalAllocationFunctionVisibility() ==
           VisibilityForcedKinds::ForceHidden;
  }

  bool allowArrayReturnTypes() const { return HLSL; }

  /// Remap path prefix according to -fmacro-prefix-path option.
  void remapPathPrefix(SmallVectorImpl<char> &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;
  }

  /// True when compiling for an offloading target device.
  bool isTargetDevice() const {
    return OpenMPIsTargetDevice || CUDAIsDevice || SYCLIsDevice;
  }

  /// Returns the most applicable C standard-compliant language version code.
  /// If none could be determined, returns \ref std::nullopt.
  std::optional<uint32_t> getCLangStd() const;

  /// Returns the most applicable C++ standard-compliant language
  /// version code.
  /// If none could be determined, returns \ref std::nullopt.
  std::optional<uint32_t> getCPlusPlusLangStd() const;
};

/// 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 FP_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 FP_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);
    setComplexRange(LO.getComplexRange());
  }

  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) {
      // C23: 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;

  void applyChanges(FPOptionsOverride FPO);

  // We can define most of the accessors automatically:
  // TODO: consider enforcing the assertion that value fits within bits
  // statically.
#define FP_OPTION(NAME, TYPE, WIDTH, PREVIOUS)                                 \
  TYPE get##NAME() const {                                                     \
    return static_cast<TYPE>((Value & NAME##Mask) >> NAME##Shift);             \
  }                                                                            \
  void set##NAME(TYPE value) {                                                 \
    assert(storage_type(value) < (1u << WIDTH));                               \
    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<storage_type>(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 setFPPreciseEnabled(bool Value) {
    setAllowFPReassociateOverride(!Value);
    setNoHonorNaNsOverride(!Value);
    setNoHonorInfsOverride(!Value);
    setNoSignedZeroOverride(!Value);
    setAllowReciprocalOverride(!Value);
    setAllowApproxFuncOverride(!Value);
    setMathErrnoOverride(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();
  }

  void setDisallowOptimizations() { setFPPreciseEnabled(true); }

  storage_type getAsOpaqueInt() const {
    return (static_cast<storage_type>(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 FP_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);
}

inline void FPOptions::applyChanges(FPOptionsOverride FPO) {
  *this = FPO.applyOverrides(*this);
}

// The three atomic code-generation options.
// The canonical (positive) names are:
//   "remote_memory", "fine_grained_memory", and "ignore_denormal_mode".
// In attribute or command-line parsing, a token prefixed with "no_" inverts its
// value.
enum class AtomicOptionKind {
  RemoteMemory,       // enable remote memory.
  FineGrainedMemory,  // enable fine-grained memory.
  IgnoreDenormalMode, // ignore floating-point denormals.
  LANGOPT_ATOMIC_OPTION_LAST = IgnoreDenormalMode,
};

struct AtomicOptions {
  // Bitfields for each option.
  unsigned remote_memory : 1;
  unsigned fine_grained_memory : 1;
  unsigned ignore_denormal_mode : 1;

  AtomicOptions()
      : remote_memory(0), fine_grained_memory(0), ignore_denormal_mode(0) {}

  AtomicOptions(const LangOptions &LO)
      : remote_memory(LO.AtomicRemoteMemory),
        fine_grained_memory(LO.AtomicFineGrainedMemory),
        ignore_denormal_mode(LO.AtomicIgnoreDenormalMode) {}

  bool getOption(AtomicOptionKind Kind) const {
    switch (Kind) {
    case AtomicOptionKind::RemoteMemory:
      return remote_memory;
    case AtomicOptionKind::FineGrainedMemory:
      return fine_grained_memory;
    case AtomicOptionKind::IgnoreDenormalMode:
      return ignore_denormal_mode;
    }
    llvm_unreachable("Invalid AtomicOptionKind");
  }

  void setOption(AtomicOptionKind Kind, bool Value) {
    switch (Kind) {
    case AtomicOptionKind::RemoteMemory:
      remote_memory = Value;
      return;
    case AtomicOptionKind::FineGrainedMemory:
      fine_grained_memory = Value;
      return;
    case AtomicOptionKind::IgnoreDenormalMode:
      ignore_denormal_mode = Value;
      return;
    }
    llvm_unreachable("Invalid AtomicOptionKind");
  }

  LLVM_DUMP_METHOD void dump() const {
    llvm::errs() << "\n remote_memory: " << remote_memory
                 << "\n fine_grained_memory: " << fine_grained_memory
                 << "\n ignore_denormal_mode: " << ignore_denormal_mode << "\n";
  }
};

/// 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 clang module.
  TU_ClangModule,

  /// 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
