//===--- OpenCLOptions.h ----------------------------------------*- 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::OpenCLOptions class.
 
///
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_CLANG_BASIC_OPENCLOPTIONS_H
 
#define LLVM_CLANG_BASIC_OPENCLOPTIONS_H
 
 
 
#include "clang/Basic/LangOptions.h"
 
#include "llvm/ADT/StringMap.h"
 
 
 
namespace clang {
 
 
 
class DiagnosticsEngine;
 
class TargetInfo;
 
 
 
namespace {
 
// This enum maps OpenCL version(s) into value. These values are used as
 
// a mask to indicate in which OpenCL version(s) extension is a core or
 
// optional core feature.
 
enum OpenCLVersionID : unsigned int {
 
  OCL_C_10 = 0x1,
 
  OCL_C_11 = 0x2,
 
  OCL_C_12 = 0x4,
 
  OCL_C_20 = 0x8,
 
  OCL_C_30 = 0x10,
 
  OCL_C_ALL = 0x1f,
 
  OCL_C_11P = OCL_C_ALL ^ OCL_C_10,              // OpenCL C 1.1+
 
  OCL_C_12P = OCL_C_ALL ^ (OCL_C_10 | OCL_C_11), // OpenCL C 1.2+
 
};
 
 
 
static inline OpenCLVersionID encodeOpenCLVersion(unsigned OpenCLVersion) {
 
  switch (OpenCLVersion) {
 
  default:
 
    llvm_unreachable("Unknown OpenCL version code");
 
  case 100:
 
    return OCL_C_10;
 
  case 110:
 
    return OCL_C_11;
 
  case 120:
 
    return OCL_C_12;
 
  case 200:
 
    return OCL_C_20;
 
  case 300:
 
    return OCL_C_30;
 
  }
 
}
 
 
 
// Check if OpenCL C version is contained in a given encoded OpenCL C version
 
// mask.
 
static inline bool isOpenCLVersionContainedInMask(const LangOptions &LO,
 
                                                  unsigned Mask) {
 
  auto CLVer = LO.getOpenCLCompatibleVersion();
 
  OpenCLVersionID Code = encodeOpenCLVersion(CLVer);
 
  return Mask & Code;
 
}
 
 
 
} // end anonymous namespace
 
 
 
/// OpenCL supported extensions and optional core features
 
class OpenCLOptions {
 
 
 
public:
 
  // OpenCL C v1.2 s6.5 - All program scope variables must be declared in the
 
  // __constant address space.
 
  // OpenCL C v2.0 s6.5.1 - Variables defined at program scope and static
 
  // variables inside a function can also be declared in the global
 
  // address space.
 
  // OpenCL C v3.0 s6.7.1 - Variables at program scope or static or extern
 
  // variables inside functions can be declared in global address space if
 
  // the __opencl_c_program_scope_global_variables feature is supported
 
  // C++ for OpenCL inherits rule from OpenCL C v2.0.
 
  bool areProgramScopeVariablesSupported(const LangOptions &Opts) const {
 
    return Opts.getOpenCLCompatibleVersion() == 200 ||
 
           (Opts.getOpenCLCompatibleVersion() == 300 &&
 
            isSupported("__opencl_c_program_scope_global_variables", Opts));
 
  }
 
 
 
  struct OpenCLOptionInfo {
 
    // Does this option have pragma.
 
    bool WithPragma = false;
 
 
 
    // Option starts to be available in this OpenCL version
 
    unsigned Avail = 100U;
 
 
 
    // Option becomes core feature in this OpenCL versions
 
    unsigned Core = 0U;
 
 
 
    // Option becomes optional core feature in this OpenCL versions
 
    unsigned Opt = 0U;
 
 
 
    // Is this option supported
 
    bool Supported = false;
 
 
 
    // Is this option enabled
 
    bool Enabled = false;
 
 
 
    OpenCLOptionInfo() = default;
 
    OpenCLOptionInfo(bool Pragma, unsigned AvailV, unsigned CoreV,
 
                     unsigned OptV)
 
        : WithPragma(Pragma), Avail(AvailV), Core(CoreV), Opt(OptV) {}
 
 
 
    bool isCore() const { return Core != 0U; }
 
 
 
    bool isOptionalCore() const { return Opt != 0U; }
 
 
 
    // Is option available in OpenCL version \p LO.
 
    bool isAvailableIn(const LangOptions &LO) const {
 
      // In C++ mode all extensions should work at least as in v2.0.
 
      return LO.getOpenCLCompatibleVersion() >= Avail;
 
    }
 
 
 
    // Is core option in OpenCL version \p LO.
 
    bool isCoreIn(const LangOptions &LO) const {
 
      return isAvailableIn(LO) && isOpenCLVersionContainedInMask(LO, Core);
 
    }
 
 
 
    // Is optional core option in OpenCL version \p LO.
 
    bool isOptionalCoreIn(const LangOptions &LO) const {
 
      return isAvailableIn(LO) && isOpenCLVersionContainedInMask(LO, Opt);
 
    }
 
  };
 
 
 
  bool isKnown(llvm::StringRef Ext) const;
 
 
 
  // For core or optional core feature check that it is supported
 
  // by a target, for any other option (extension) check that it is
 
  // enabled via pragma
 
  bool isAvailableOption(llvm::StringRef Ext, const LangOptions &LO) const;
 
 
 
  bool isWithPragma(llvm::StringRef Ext) const;
 
 
 
  // Is supported as either an extension or an (optional) core feature for
 
  // OpenCL version \p LO.
 
  bool isSupported(llvm::StringRef Ext, const LangOptions &LO) const;
 
 
 
  // Is supported OpenCL core feature for OpenCL version \p LO.
 
  // For supported extension, return false.
 
  bool isSupportedCore(llvm::StringRef Ext, const LangOptions &LO) const;
 
 
 
  // Is supported optional core OpenCL feature for OpenCL version \p LO.
 
  // For supported extension, return false.
 
  bool isSupportedOptionalCore(llvm::StringRef Ext,
 
                               const LangOptions &LO) const;
 
 
 
  // Is supported optional core or core OpenCL feature for OpenCL version \p
 
  // LO. For supported extension, return false.
 
  bool isSupportedCoreOrOptionalCore(llvm::StringRef Ext,
 
                                     const LangOptions &LO) const;
 
 
 
  // Is supported OpenCL extension for OpenCL version \p LO.
 
  // For supported core or optional core feature, return false.
 
  bool isSupportedExtension(llvm::StringRef Ext, const LangOptions &LO) const;
 
 
 
  // FIXME: Whether extension should accept pragma should not
 
  // be reset dynamically. But it currently required when
 
  // registering new extensions via pragmas.
 
  void acceptsPragma(llvm::StringRef Ext, bool V = true);
 
 
 
  void enable(llvm::StringRef Ext, bool V = true);
 
 
 
  /// Enable or disable support for OpenCL extensions
 
  /// \param Ext name of the extension (not prefixed with '+' or '-')
 
  /// \param V value to set for a extension
 
  void support(llvm::StringRef Ext, bool V = true);
 
 
 
  OpenCLOptions();
 
 
 
  // Set supported options based on target settings and language version
 
  void addSupport(const llvm::StringMap<bool> &FeaturesMap,
 
                  const LangOptions &Opts);
 
 
 
  // Disable all extensions
 
  void disableAll();
 
 
 
  friend class ASTWriter;
 
  friend class ASTReader;
 
 
 
  using OpenCLOptionInfoMap = llvm::StringMap<OpenCLOptionInfo>;
 
 
 
  template <typename... Args>
 
  static bool isOpenCLOptionCoreIn(const LangOptions &LO, Args &&... args) {
 
    return OpenCLOptionInfo(std::forward<Args>(args)...).isCoreIn(LO);
 
  }
 
 
 
  template <typename... Args>
 
  static bool isOpenCLOptionAvailableIn(const LangOptions &LO,
 
                                        Args &&... args) {
 
    return OpenCLOptionInfo(std::forward<Args>(args)...).isAvailableIn(LO);
 
  }
 
 
 
  // Diagnose feature dependencies for OpenCL C 3.0. Return false if target
 
  // doesn't follow these requirements.
 
  static bool diagnoseUnsupportedFeatureDependencies(const TargetInfo &TI,
 
                                                     DiagnosticsEngine &Diags);
 
 
 
  // Diagnose that features and equivalent extension are set to same values.
 
  // Return false if target doesn't follow these requirements.
 
  static bool diagnoseFeatureExtensionDifferences(const TargetInfo &TI,
 
                                                  DiagnosticsEngine &Diags);
 
 
 
private:
 
  // Option is enabled via pragma
 
  bool isEnabled(llvm::StringRef Ext) const;
 
 
 
  OpenCLOptionInfoMap OptMap;
 
};
 
 
 
} // end namespace clang
 
 
 
#endif