//===- subzero/src/IceClFlags.h - Cl Flags for translation ------*- C++ -*-===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Declares Ice::ClFlags which implements command line processing.
///
//===----------------------------------------------------------------------===//

#ifndef SUBZERO_SRC_ICECLFLAGS_H
#define SUBZERO_SRC_ICECLFLAGS_H

#include "IceDefs.h"
#include "IceBuildDefs.h"
#include "IceClFlags.def"
#include "IceRangeSpec.h"
#include "IceTypes.h"

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif // __clang__

#include "llvm/IRReader/IRReader.h"

#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__

#include <string>
#include <utility>
#include <vector>

#ifndef PNACL_LLVM
namespace llvm {
// \brief Define the expected format of the file.
enum NaClFileFormat {
  // LLVM IR source or bitcode file (as appropriate).
  LLVMFormat,
  // PNaCl bitcode file.
  PNaClFormat,
  // Autodetect if PNaCl or LLVM format.
  AutodetectFileFormat
};
} // end of namespace llvm
#endif // !PNACL_LLVM

namespace Ice {
// detail defines the type cl_type_traits, which is used to define the
// getters/setters for the ClFlags class. It converts the cl_detail::*_flag
// types to appropriate types for the several getters and setters created.
namespace detail {
// Base cl_type_traits.
template <typename B, typename CL> struct cl_type_traits {};

// cl_type_traits specialized cl::list<std::string>, non-MINIMAL build.
template <> struct cl_type_traits<std::string, cl_detail::dev_list_flag> {
  using storage_type = std::vector<std::string>;
};

// cl_type_traits specialized cl::list<Ice::VerboseItem>, non-MINIMAL build.
template <> struct cl_type_traits<Ice::VerboseItem, cl_detail::dev_list_flag> {
  using storage_type = Ice::VerboseMask;
};

// cl_type_traits specialized cl::opt<T>, non-MINIMAL build.
template <typename T> struct cl_type_traits<T, cl_detail::dev_opt_flag> {
  using storage_type = T;
};

// cl_type_traits specialized cl::opt<T>, MINIMAL build.
template <typename T> struct cl_type_traits<T, cl_detail::release_opt_flag> {
  using storage_type = T;
};

} // end of namespace detail

/// Define variables which configure translation and related support functions.
class ClFlags {
  ClFlags(const ClFlags &) = delete;
  ClFlags &operator=(const ClFlags &) = delete;

public:
  /// User defined constructor.
  ClFlags() { resetClFlags(); }

  /// The command line flags.
  static ClFlags Flags;

  /// \brief Parse commmand line options for Subzero.
  ///
  /// This is done use cl::ParseCommandLineOptions() and the static variables of
  /// type cl::opt defined in IceClFlags.cpp
  static void parseFlags(int argc, const char *const *argv);

  /// Reset all configuration options to their nominal values.
  void resetClFlags();

  /// \brief Retrieve the configuration option state
  ///
  /// This is defined by static variables
  /// anonymous_namespace{IceClFlags.cpp}::AllowErrorRecoveryObj,
  /// anonymous_namespace{IceClFlags.cpp}::AllowIacaMarksObj,
  /// ...
  static void getParsedClFlags(ClFlags &OutFlags);

#define X(Name, Type, ClType, ...)                                             \
private:                                                                       \
  using Name##StorageType =                                                    \
      detail::cl_type_traits<Type, cl_detail::ClType>::storage_type;           \
                                                                               \
  Name##StorageType Name;                                                      \
                                                                               \
  template <bool E>                                                            \
  typename std::enable_if<E, void>::type set##Name##Impl(                      \
      Name##StorageType Value) {                                               \
    Name = std::move(Value);                                                   \
  }                                                                            \
                                                                               \
  template <bool E>                                                            \
  typename std::enable_if<!E, void>::type set##Name##Impl(Name##StorageType) { \
  }                                                                            \
                                                                               \
public:                                                                        \
  Name##StorageType get##Name() const { return Name; }                         \
  void set##Name(Name##StorageType Value) {                                    \
    /* TODO(jpp): figure out which optional flags are used in minimal, and     \
       what are the defaults for them. */                                      \
    static constexpr bool Enable =                                             \
        std::is_same<cl_detail::ClType, cl_detail::release_opt_flag>::value || \
        !BuildDefs::minimal() || true;                                         \
    set##Name##Impl<Enable>(std::move(Value));                                 \
  }                                                                            \
                                                                               \
private:
  COMMAND_LINE_FLAGS
#undef X

public:
  bool isSequential() const { return NumTranslationThreads == 0; }
  bool isParseParallel() const {
    return getParseParallel() && !isSequential() && getBuildOnRead();
  }
  std::string getAppName() const { return AppName; }
  void setAppName(const std::string &Value) { AppName = Value; }

  /// \brief Get the value of ClFlags::GenerateUnitTestMessages
  ///
  /// Note: If dump routines have been turned off, the error messages
  /// will not be readable. Hence, turn off.
  bool getGenerateUnitTestMessages() const {
    return !BuildDefs::dump() || GenerateUnitTestMessages;
  }
  /// Set ClFlags::GenerateUnitTestMessages to a new value
  void setGenerateUnitTestMessages(bool NewValue) {
    GenerateUnitTestMessages = NewValue;
  }
  bool matchForceO2(GlobalString Name, uint32_t Number) const {
    return ForceO2.match(Name, Number);
  }
  bool matchSplitInsts(const std::string &Name, uint32_t Number) const {
    return SplitInsts.match(Name, Number);
  }
  bool matchTestStatus(GlobalString Name, uint32_t Number) const {
    return TestStatus.match(Name, Number);
  }
  bool matchTimingFocus(GlobalString Name, uint32_t Number) const {
    return TimingFocus.match(Name, Number);
  }
  bool matchTranslateOnly(GlobalString Name, uint32_t Number) const {
    return TranslateOnly.match(Name, Number);
  }
  bool matchVerboseFocusOn(GlobalString Name, uint32_t Number) const {
    return VerboseFocus.match(Name, Number);
  }
  bool matchVerboseFocusOn(const std::string &Name, uint32_t Number) const {
    return VerboseFocus.match(Name, Number);
  }

private:
  std::string AppName;

  /// Initialized to false; not set by the command line.
  bool GenerateUnitTestMessages;

  RangeSpec ForceO2;
  RangeSpec SplitInsts;
  RangeSpec TestStatus;
  RangeSpec TimingFocus;
  RangeSpec TranslateOnly;
  RangeSpec VerboseFocus;
};

inline const ClFlags &getFlags() { return ClFlags::Flags; }

} // end of namespace Ice

#endif // SUBZERO_SRC_ICECLFLAGS_H