You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tvm.apache.org by an...@apache.org on 2022/09/17 00:01:31 UTC

[tvm] 13/18: llvm instance optional

This is an automated email from the ASF dual-hosted git repository.

andrewzhaoluo pushed a commit to branch aluo/rebase-09162022-autotensorization
in repository https://gitbox.apache.org/repos/asf/tvm.git

commit 848d5a1c29f38c2b1464927a4e54c9f712637149
Author: Andrew Zhao Luo <an...@gmail.com>
AuthorDate: Fri Sep 2 14:59:23 2022 -0700

    llvm instance optional
---
 src/target/llvm/llvm_instance.cc | 430 ++-------------------------------------
 src/target/llvm/llvm_instance.h  | 194 ++++--------------
 src/target/llvm/llvm_module.cc   |   8 +-
 3 files changed, 56 insertions(+), 576 deletions(-)

diff --git a/src/target/llvm/llvm_instance.cc b/src/target/llvm/llvm_instance.cc
index 19ff480452..772e71b287 100644
--- a/src/target/llvm/llvm_instance.cc
+++ b/src/target/llvm/llvm_instance.cc
@@ -39,7 +39,6 @@
 #include <llvm/Support/TargetRegistry.h>
 #endif
 #include <llvm/Support/CodeGen.h>
-#include <llvm/Support/CommandLine.h>
 #include <llvm/Support/ErrorOr.h>
 #include <llvm/Support/Host.h>
 #include <llvm/Support/MemoryBuffer.h>
@@ -57,14 +56,9 @@
 #include <tvm/target/target.h>
 
 #include <atomic>
-#include <cctype>
-#include <memory>
-#include <optional>
-#include <ostream>
 #include <sstream>
 #include <string>
 #include <system_error>
-#include <utility>
 
 namespace tvm {
 namespace codegen {
@@ -142,27 +136,10 @@ std::unique_ptr<llvm::Module> LLVMInstance::ParseBuffer(const llvm::MemoryBuffer
   return module;
 }
 
-// LLVMTargetInfo
-
-std::ostream& operator<<(std::ostream& os, const LLVMTargetInfo::Option& opt) {
-  os << '-' << opt.name;
-  switch (opt.type) {
-    case LLVMTargetInfo::Option::OptType::Bool:
-      return os << ":bool=" << (opt.value.b ? "true" : "false");
-    case LLVMTargetInfo::Option::OptType::Int:
-      return os << ":int=" << opt.value.i;
-    case LLVMTargetInfo::Option::OptType::UInt:
-      return os << ":uint=" << opt.value.u;
-    case LLVMTargetInfo::Option::OptType::String:
-      return os << ":string=" << opt.value.s;
-    default:
-      os << ":?(" << static_cast<int>(opt.type) << ")";
-      break;
-  }
-  return os;
-}
+// LLVMTarget
 
-LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) {
+LLVMTarget::LLVMTarget(LLVMInstance& instance, const Target& target)
+    : instance_(instance), ctx_(instance.GetContext()) {
   triple_ = target->GetAttr<String>("mtriple").value_or("default");
 
   if (triple_.empty() || triple_ == "default") {
@@ -176,26 +153,6 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) {
     }
   }
 
-  if (const Optional<Array<String>>& v = target->GetAttr<Array<String>>("cl-opt")) {
-    llvm::StringMap<llvm::cl::Option*>& options = llvm::cl::getRegisteredOptions();
-    bool parse_error = false;
-    for (const String& s : v.value()) {
-      Option opt = ParseOptionString(s);
-      if (opt.type == Option::OptType::Invalid) {
-        parse_error = true;
-        continue;
-      }
-      if (options.count(opt.name)) {
-        llvm_options_.push_back(opt);
-      } else {
-        // Flag an error, but don't abort. LLVM flags may change, and this would
-        // give the code a chance to run even if the option no longer applies.
-        LOG(ERROR) << "\"" << opt.name << "\" is not an LLVM option, option ignored";
-      }
-    }
-    ICHECK(!parse_error) << "there were errors parsing command-line options";
-  }
-
   llvm::FloatABI::ABIType float_abi = llvm::FloatABI::Default;
   if (const Optional<String>& v = target->GetAttr<String>("mfloat-abi")) {
     String value = v.value();
@@ -281,12 +238,17 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) {
   }
 }
 
-LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& scope, const std::string& target_str)
-    : LLVMTargetInfo(scope, Target(target_str)) {}
+LLVMTarget::LLVMTarget(LLVMInstance& scope, const std::string& target_str)
+    : LLVMTarget(scope, Target(target_str)) {}
 
-LLVMTargetInfo::~LLVMTargetInfo() = default;
+LLVMTarget::~LLVMTarget() = default;
+
+llvm::LLVMContext* LLVMTarget::GetContext() const {
+  ICHECK(!ctx_.expired()) << "LLVM scope has been deleted";
+  return ctx_.lock().get();
+}
 
-llvm::TargetMachine* LLVMTargetInfo::GetOrCreateTargetMachine(bool allow_missing) {
+llvm::TargetMachine* LLVMTarget::GetOrCreateTargetMachine(bool allow_missing) {
   if (target_machine_) return target_machine_.get();
 
   std::string error;
@@ -302,11 +264,11 @@ llvm::TargetMachine* LLVMTargetInfo::GetOrCreateTargetMachine(bool allow_missing
   return target_machine_.get();
 }
 
-std::string LLVMTargetInfo::GetTargetFeatureString() const {  //
+std::string LLVMTarget::GetTargetFeatureString() const {  //
   return Join(",", attrs_);
 }
 
-std::string LLVMTargetInfo::str() const {
+std::string LLVMTarget::str() const {
   std::ostringstream os;
   os << "llvm";
   if (!triple_.empty()) {
@@ -378,324 +340,9 @@ std::string LLVMTargetInfo::str() const {
     }
   }
 
-  if (size_t num = llvm_options_.size(); num > 0) {
-    os << " -cl-opt=";
-    std::vector<std::string> opts;
-    for (const Option& opt : llvm_options_) {
-      std::stringstream os;
-      os << opt;
-      opts.emplace_back(os.str());
-    }
-    auto* quote = num > 1 ? "'" : "";
-    os << quote << Join(",", opts) << quote;
-  }
-
   return os.str();
 }
 
-LLVMTargetInfo::Option LLVMTargetInfo::ParseOptionString(const std::string& str) {
-  Option opt;
-  opt.type = Option::OptType::Invalid;
-
-  // Option string: "-"+ <option_name> ":" <type> "=" <value>
-  //
-  // Note: "-"+ means 1 or more dashes, but only "-" are "--" valid.
-
-  // The first step is to do "lexing" of the option string, i.e. to break
-  // it up into parts (like "tokens") according to the syntax above. These
-  // parts will be non-overlapping substrings of the option string, and
-  // concatenated together, they will be equal to the option string.
-  // The literal elements are parts on their own.
-  //
-  // Note that the option string may be malformed, so any of the literal
-  // elements in the syntax may be missing.
-
-  std::vector<std::string> parts;
-
-  auto find_first_of = [](const std::string& str, const std::string& chars, auto start = 0) {
-    auto pos = str.find_first_of(chars, start);
-    return pos != std::string::npos ? pos : str.size();
-  };
-  auto find_first_not_of = [](const std::string& str, const std::string& chars, auto start = 0) {
-    auto pos = str.find_first_not_of(chars, start);
-    return pos != std::string::npos ? pos : str.size();
-  };
-
-  // "-"+
-  std::string::size_type pos_start = 0, pos_end = str.size();
-  std::string::size_type pos_at = find_first_not_of(str, "-", pos_start);
-  if (pos_at > 0) {
-    parts.push_back(str.substr(pos_start, pos_at));
-  }
-  // <option_name>, always present, may be empty string
-  pos_start = pos_at;
-  pos_at = find_first_of(str, ":=", pos_start);
-  parts.push_back(str.substr(pos_start, pos_at - pos_start));
-
-  // ":" or "=", if any
-  pos_start = pos_at;
-  char c = pos_start < pos_end ? str[pos_start] : 0;
-  if (c != 0) {
-    parts.emplace_back(1, c);
-    pos_start++;
-  }
-  // If the character found in the previous step wasn't '=', look for '='.
-  if (c == ':') {
-    // <type>
-    pos_at = find_first_of(str, "=", pos_start);
-    if (pos_at > pos_start) {  // if non-empty
-      parts.push_back(str.substr(pos_start, pos_at - pos_start));
-    }
-
-    // "="
-    if (pos_at < pos_end) {
-      parts.emplace_back(1, str[pos_at]);
-      pos_start = pos_at + 1;
-    }
-  }
-  if (pos_start < pos_end) {
-    // <value>
-    parts.push_back(str.substr(pos_start));
-  }
-
-  // After breaking up the option string, examine and validate the individual
-  // parts.
-
-  int part_this = 0, part_end = parts.size();
-
-  const std::string error_header = "while parsing option \"" + str + "\": ";
-
-  // Check for "-" or "--".
-  if (part_this < part_end) {
-    auto& p = parts[part_this++];
-    if ((p.size() != 1 && p.size() != 2) || p.find_first_not_of('-') != std::string::npos) {
-      LOG(ERROR) << error_header << "option must start with \"-\" or \"--\"";
-      return opt;
-    }
-  }
-
-  // Validate option name.
-  if (part_this < part_end) {
-    auto& p = parts[part_this++];
-    if (p.empty()) {
-      LOG(ERROR) << error_header << "option name must not be empty";
-      return opt;
-    }
-    opt.name = std::move(p);
-  }
-
-  // Check type, if present.
-  Option::OptType type = Option::OptType::Invalid;
-  if (part_this < part_end) {
-    auto& p0 = parts[part_this];
-    if (p0 == ":") {
-      part_this++;  // Only advance if we saw ":".
-      if (part_this < part_end) {
-        auto& p1 = parts[part_this];
-        ICHECK(!p1.empty()) << "tokenizing error";  // This shouldn't happen.
-        if (p1 != "=") {
-          part_this++;
-          if (p1 == "bool") {
-            type = Option::OptType::Bool;
-          } else if (p1 == "int") {
-            type = Option::OptType::Int;
-          } else if (p1 == "uint") {
-            type = Option::OptType::UInt;
-          } else if (p1 == "string") {
-            type = Option::OptType::String;
-          }
-        }
-      }
-      // If there was ":", there must be a type.
-      if (type == Option::OptType::Invalid) {
-        LOG(ERROR) << error_header << "invalid type";
-        return opt;
-      }
-    }
-  }
-
-  // Check value, if present.
-  std::optional<std::string> value;
-  if (part_this < part_end) {
-    auto& p0 = parts[part_this];
-    if (p0 == "=") {
-      part_this++;
-      if (part_this < part_end) {
-        value = std::move(parts[part_this]);
-      } else {
-        value = "";
-      }
-    } else {
-      // If there are still any parts left to be processed, there must be "=".
-      LOG(ERROR) << error_header << "expecting \"=\"";
-      return opt;
-    }
-  }
-
-  // NOLINTNEXTLINE(runtime/int)
-  auto to_integer = [](const std::string& s) -> std::optional<long long> {
-    // std::stoll takes "long long"
-    long long number;  // NOLINT(runtime/int)
-    size_t pos;
-    try {
-      number = std::stoll(s, &pos);
-    } catch (...) {
-      return std::nullopt;
-    }
-    if (pos == s.size()) {
-      return number;
-    } else {
-      return std::nullopt;
-    }
-  };
-
-  auto to_boolean = [&to_integer](const std::string& s) -> std::optional<bool> {
-    // Return 0 or 1, if string corresponds to a valid boolean value,
-    // otherwise return 2.
-    auto ti = to_integer(s);
-    if (ti.has_value() && (ti.value() == 0 || ti.value() == 1)) {
-      return static_cast<bool>(ti.value());
-    }
-
-    std::string lower;
-    std::transform(s.begin(), s.end(), std::back_inserter(lower),
-                   [](unsigned char c) { return std::tolower(c); });
-    if (lower == "true") {
-      return true;
-    } else if (lower == "false") {
-      return false;
-    }
-    return std::nullopt;
-  };
-
-  if (value.has_value()) {
-    if (type == Option::OptType::Int || type == Option::OptType::UInt) {
-      auto v = to_integer(value.value());
-      if (!v.has_value()) {
-        LOG(ERROR) << error_header << "invalid integer value \"" << value.value() << "\"";
-        return opt;
-      }
-      if (type == Option::OptType::Int) {
-        opt.value.i = static_cast<int>(v.value());
-        if (opt.value.i != v.value()) {
-          LOG(WARNING) << error_header << "value exceeds int range, assuming " << opt.value.i;
-        }
-      } else {
-        // NOLINTNEXTLINE(runtime/int)
-        opt.value.u = static_cast<unsigned>(static_cast<unsigned long long>(v.value()));
-        if (opt.value.u != static_cast<unsigned long long>(v.value())) {  // NOLINT(runtime/int)
-          LOG(WARNING) << error_header << "value exceeds int range, assuming " << opt.value.u;
-        }
-      }
-    } else if (type == Option::OptType::String) {
-      opt.value.s = std::move(value.value());
-    } else {
-      // "type" is either Bool (given explicitly) or Invalid (type not present in string)
-      auto v = to_boolean(value.value());
-      if (!v.has_value()) {
-        LOG(ERROR) << error_header << "invalid boolean value \"" << value.value() << "\"";
-        return opt;
-      }
-      opt.value.b = v.value();
-      type = Option::OptType::Bool;
-    }
-  } else {
-    // Value was not present in string. Assume "true" if "type" is Bool or Invalid
-    if (type == Option::OptType::Bool || type == Option::OptType::Invalid) {
-      opt.value.b = true;
-      type = Option::OptType::Bool;
-    } else {
-      LOG(ERROR) << error_header << "must have a value";
-      return opt;
-    }
-  }
-
-  ICHECK(type != Option::OptType::Invalid);
-  opt.type = type;
-  return opt;
-}
-
-bool LLVMTargetInfo::MatchesGlobalState() const {
-  for (const Option& opt : GetCommandLineOptions()) {
-    Option current_opt = opt;
-    GetOptionValue(&current_opt);
-    ICHECK(current_opt.type != Option::OptType::Invalid);
-    switch (current_opt.type) {
-      case Option::OptType::Bool:
-        if (current_opt.value.b != opt.value.b) return false;
-        continue;
-      case Option::OptType::Int:
-        if (current_opt.value.i != opt.value.i) return false;
-        continue;
-      case Option::OptType::UInt:
-        if (current_opt.value.u != opt.value.u) return false;
-        continue;
-      case Option::OptType::String:
-        if (current_opt.value.s != opt.value.s) return false;
-        continue;
-      default:;  // NOLINT(whitespace/semicolon)
-    }
-  }
-  return true;
-}
-
-void LLVMTargetInfo::GetOptionValue(LLVMTargetInfo::Option* opt) const {
-  llvm::StringMap<llvm::cl::Option*>& options = llvm::cl::getRegisteredOptions();
-  llvm::cl::Option* base_op = options[opt->name];
-
-  if (opt->type == Option::OptType::Bool) {
-    auto* bool_op = static_cast<llvm::cl::opt<bool>*>(base_op);
-    opt->value.b = bool_op->getValue();
-  } else if (opt->type == Option::OptType::Int) {
-    auto* int_op = static_cast<llvm::cl::opt<int>*>(base_op);
-    opt->value.i = int_op->getValue();
-  } else if (opt->type == Option::OptType::UInt) {
-    auto* uint_op = static_cast<llvm::cl::opt<unsigned>*>(base_op);
-    opt->value.u = uint_op->getValue();
-  } else if (opt->type == Option::OptType::String) {
-    auto* str_op = static_cast<llvm::cl::opt<std::string>*>(base_op);
-    opt->value.s = str_op->getValue();
-  } else {
-    opt->type = Option::OptType::Invalid;
-  }
-}
-
-// LLVMTarget
-
-bool LLVMTarget::modified_llvm_state_ = false;
-
-LLVMTarget::LLVMTarget(LLVMInstance& instance, const LLVMTargetInfo& target_info)
-    : LLVMTargetInfo(target_info), instance_(instance), ctx_(instance.GetContext()) {
-  // Populate the list of saved options with the current values.
-  for (const Option& opt : GetCommandLineOptions()) {
-    GetOptionValue(&saved_llvm_options_.emplace_back(opt));
-  }
-
-  if (modified_llvm_state_) {
-    ICHECK(!ApplyLLVMOptions(true));
-  } else {
-    modified_llvm_state_ = ApplyLLVMOptions(true);
-  }
-}
-
-LLVMTarget::LLVMTarget(LLVMInstance& instance, const Target& target)
-    : LLVMTarget(instance, LLVMTargetInfo(instance, target)) {}
-
-LLVMTarget::LLVMTarget(LLVMInstance& scope, const std::string& target_str)
-    : LLVMTarget(scope, Target(target_str)) {}
-
-LLVMTarget::~LLVMTarget() {
-  // Revert all applied LLVM options.
-  if (ApplyLLVMOptions(false)) {
-    modified_llvm_state_ = false;
-  }
-}
-
-llvm::LLVMContext* LLVMTarget::GetContext() const {
-  ICHECK(!ctx_.expired()) << "LLVM scope has been deleted";
-  return ctx_.lock().get();
-}
-
 std::string LLVMTarget::GetTargetMetadata(const llvm::Module& module) {
   if (llvm::Metadata* tvm_target = module.getModuleFlag("tvm_target")) {
     auto* mdstr = llvm::cast<llvm::MDString>(tvm_target);
@@ -712,55 +359,6 @@ void LLVMTarget::SetTargetMetadata(llvm::Module* module) const {
                         llvm::MDString::get(*GetContext(), str()));
 }
 
-bool LLVMTarget::ApplyLLVMOptions(bool apply_otherwise_revert, bool dry_run) {
-  llvm::StringMap<llvm::cl::Option*>& options = llvm::cl::getRegisteredOptions();
-  bool changed = false;
-
-#define HANDLE_OPTION_VALUE(option, new_val, saved_val)                  \
-  do {                                                                   \
-    auto current = (option)->getValue();                                 \
-    auto replacement = apply_otherwise_revert ? (new_val) : (saved_val); \
-    if (current != replacement) {                                        \
-      changed = true;                                                    \
-      if (!dry_run) {                                                    \
-        (option)->setValue(replacement);                                 \
-      }                                                                  \
-    }                                                                    \
-  } while (false);
-
-  const auto& new_options = GetCommandLineOptions();
-  for (size_t i = 0, e = saved_llvm_options_.size(); i != e; ++i) {
-    const Option& new_opt = new_options[i];
-    const Option& saved_opt = saved_llvm_options_[i];
-
-    llvm::cl::Option* base_op = options[new_opt.name];
-
-    if (new_opt.type == Option::OptType::Bool) {
-      auto* bool_op = static_cast<llvm::cl::opt<bool>*>(base_op);
-      HANDLE_OPTION_VALUE(bool_op, new_opt.value.b, saved_opt.value.b);
-    } else if (new_opt.type == Option::OptType::Int) {
-      auto* int_op = static_cast<llvm::cl::opt<int>*>(base_op);
-      HANDLE_OPTION_VALUE(int_op, new_opt.value.i, saved_opt.value.i);
-    } else if (new_opt.type == Option::OptType::UInt) {
-      auto* uint_op = static_cast<llvm::cl::opt<unsigned>*>(base_op);
-      HANDLE_OPTION_VALUE(uint_op, new_opt.value.u, saved_opt.value.u);
-    } else if (new_opt.type == Option::OptType::String) {
-      auto* str_op = static_cast<llvm::cl::opt<std::string>*>(base_op);
-      HANDLE_OPTION_VALUE(str_op, new_opt.value.s, saved_opt.value.s);
-    } else {
-      LOG(FATAL) << "unexpected type in option " << new_opt;
-    }
-
-    if (dry_run && changed) {
-      return true;
-    }
-  }
-
-#undef HANDLE_OPTION_VALUE
-
-  return changed;
-}
-
 }  // namespace codegen
 }  // namespace tvm
 
diff --git a/src/target/llvm/llvm_instance.h b/src/target/llvm/llvm_instance.h
index 217db63aad..afb6e58deb 100644
--- a/src/target/llvm/llvm_instance.h
+++ b/src/target/llvm/llvm_instance.h
@@ -38,7 +38,6 @@
 #include <tvm/runtime/container/string.h>
 #include <tvm/target/target.h>
 
-#include <algorithm>
 #include <memory>
 #include <string>
 #include <utility>
@@ -58,9 +57,8 @@ class LLVMTarget;
 
 /*!
  * \class LLVMInstance
- * \brief LLVMInstance is a class that (conceptually) starts and stops LLVM.
- *        All uses of LLVM should take place within a lifetime of an object
- *        of this class.
+ * \brief LLVMInstance is a class that (conceptually) starts and stops LLVM. All
+ * uses of LLVM should take place within a lifetime of an object of this class.
  *
  * E.g.
  * ```{.cpp}
@@ -130,48 +128,60 @@ class LLVMInstance {
 };
 
 /*!
- * \class LLVMTargetInfo
- * \brief Summary of information for this TVM target relevant to LLVM code
- *        generation.
+ * \class LLVMTarget
+ * \brief Information used by LLVM for code generation for particular target
  *
  * This class contains all information that LLVM needs for code generation for
- * a particular target. The purpose of this class is only to provide information
- * in an easily-accessible form (for example for querying the target properties).
+ * a particular target. Since Target in TVM will soon contain command line
+ * flags for LLVM, objects of this class will handle saving and restoring
+ * global LLVM state that may be affected by these flags. This way, code
+ * generation for each LLVM-based target in TVM will start with the same LLVM
+ * global state.
  *
  * Note that objects of this class must be created within the lifetime of an
  * LLVMInstance object.
  */
-class LLVMTargetInfo {
+class LLVMTarget {
  public:
   /*!
-   * \brief Constructs LLVMTargetInfo from `Target`
+   * \brief Constructs LLVMTarget from `Target`
    * \param scope LLVMInstance object
    * \param target TVM Target object for target "llvm"
    */
-  LLVMTargetInfo(LLVMInstance& scope, const Target& target);  // NOLINT(runtime/references)
+  LLVMTarget(LLVMInstance& scope, const Target& target);  // NOLINT(runtime/references)
   /*!
-   * \brief Constructs LLVMTargetInfo from target string
+   * \brief Constructs LLVMTarget from target string
    * \param scope LLVMInstance object
    * \param target TVM target string for target "llvm"
    */
-  // NOLINTNEXTLINE(runtime/references)
-  LLVMTargetInfo(LLVMInstance& scope, const std::string& target_str);
+  LLVMTarget(LLVMInstance& scope, const std::string& target_str);  // NOLINT(runtime/references)
   /*!
-   * \brief Destroys LLVMTargetInfo object
+   * \brief Destroys LLVMTarget object
    */
-  ~LLVMTargetInfo();
+  ~LLVMTarget();
 
   /*!
-   * \brief Returns string representation (as TVM target) of the LLVMTargetInfo
+   * \brief Returns string representation (as TVM target) of the LLVMTarget
    * \return Target string
    *
-   * Note: If the LLVMTargetInfo object was created from a string `s`, the string
+   * Note: If the LLVMTarget object was created from a string `s`, the string
    * returned here may not be exactly equal to `s`. For example, if the CPU
    * was "default", the returned string will have CPU set to the detected host
    * CPU.
    */
   std::string str() const;
 
+  /*!
+   * \brief Get the LLVMInstance object from which the LLVMTarget object was
+   *        created
+   * \return The enclosing LLVMInstance object
+   */
+  const LLVMInstance& GetInstance() const { return instance_; }
+  /*!
+   * \brief Get the current LLVM context
+   * \return the current LLVM context
+   */
+  llvm::LLVMContext* GetContext() const;
   /*!
    * \brief Return LLVM's `TargetMachine`, or nullptr
    * \param allow_missing do not abort if the target machine cannot be created,
@@ -218,125 +228,6 @@ class LLVMTargetInfo {
    */
   llvm::CodeGenOpt::Level GetOptLevel() const { return opt_level_; }
 
-  /*!
-   * \class Option
-   * \brief Internal representation of command-line option
-   */
-  struct Option {
-    enum class OptType {
-      Invalid = 0,  //!< placeholder, indicates parsing error
-      Bool,         //!< enum value corresponding to type string "bool"
-      Int,          //!< enum value corresponding to type string "int"
-      UInt,         //!< enum value corresponding to type string "uint"
-      String,       //!< enum value corresponding to type string "string"
-    };
-    std::string name;  //!< option name
-    OptType type;      //!< type of the option value
-    struct {
-      union {
-        bool b;          //!< bool option value
-        int i;           //!< int option value
-        unsigned u = 0;  //!< unsigned option value
-      };
-      std::string s;  //!< string option value
-    } value;          //!< option value specified in the option string
-  };
-
-  /*!
-   * \brief Get LLVM command line options
-   * \return the list of LLVM command line options specified for this target
-   */
-  const std::vector<Option>& GetCommandLineOptions() const { return llvm_options_; }
-
-  /*!
-   * \brief Parse a string from the `cl-opt` target attribute
-   * \param str the option string
-   * \return parsed `Option` object, if parsing failed the type member will be
-   *         set to `Option::OptType::Invalid`
-   */
-  static Option ParseOptionString(const std::string& str);
-
-  /*!
-   * \brief Checks if the settings in this object that describe global state
-   *        match the current global state
-   * \return true or false correspondingly
-   * \note The global state can be modified by command line options. This
-   *       function checks if the specified options differ from their current
-   *       values.
-   */
-  bool MatchesGlobalState() const;
-
- protected:
-  /*!
-   * \brief Get the current value of given LLVM option
-   * \param opt Option with "type" and "name" set
-   * Fills in the "value" field in the provided Option argument, or sets the
-   * "type" to Invalid if the option value cannot be obtained.
-   */
-  void GetOptionValue(Option* opt) const;
-
- private:
-  std::string triple_;
-  std::string cpu_;
-  std::vector<std::string> attrs_;
-  std::vector<Option> llvm_options_;
-  llvm::TargetOptions target_options_;
-  llvm::FastMathFlags fast_math_flags_;
-  llvm::CodeGenOpt::Level opt_level_;
-  llvm::Reloc::Model reloc_model_ = llvm::Reloc::PIC_;
-  llvm::CodeModel::Model code_model_ = llvm::CodeModel::Small;
-  std::shared_ptr<llvm::TargetMachine> target_machine_;
-};
-
-/*!
- * \class LLVMTarget
- * \brief Information used by LLVM for code generation for particular target
- *
- * In addition to all information that LLVM needs for code generation for
- * a particular target, objects of this class handle saving and restoring
- * global LLVM state that may be affected by these flags. This way, code
- * generation for each LLVM-based target in TVM will start with the same LLVM
- * global state.
- *
- * Note that objects of this class must be created within the lifetime of an
- * LLVMInstance object.
- */
-class LLVMTarget : public LLVMTargetInfo {
- public:
-  /*!
-   * \brief Constructs LLVMTarget from `Target`
-   * \param scope LLVMInstance object
-   * \param target_info Target info object for target "llvm"
-   */
-  LLVMTarget(LLVMInstance& scope, const LLVMTargetInfo& target_info);  // NOLINT(runtime/references)
-  /*!
-   * \brief Constructs LLVMTarget from `Target`
-   * \param scope LLVMInstance object
-   * \param target TVM Target object for target "llvm"
-   */
-  LLVMTarget(LLVMInstance& scope, const Target& target);  // NOLINT(runtime/references)
-  /*!
-   * \brief Constructs LLVMTarget from target string
-   * \param scope LLVMInstance object
-   * \param target TVM target string for target "llvm"
-   */
-  LLVMTarget(LLVMInstance& scope, const std::string& target_str);  // NOLINT(runtime/references)
-  /*!
-   * \brief Destroys LLVMTarget object
-   */
-  ~LLVMTarget();
-
-  /*!
-   * \brief Get the LLVMInstance object from which the LLVMTarget object was
-   *        created
-   * \return The enclosing LLVMInstance object
-   */
-  const LLVMInstance& GetInstance() const { return instance_; }
-  /*!
-   * \brief Get the current LLVM context
-   * \return the current LLVM context
-   */
-  llvm::LLVMContext* GetContext() const;
   /*!
    * \brief Extract the target string from given `llvm::Module`
    * \param module LLVM module with the TVM target string embedded as metadata
@@ -354,27 +245,18 @@ class LLVMTarget : public LLVMTargetInfo {
   void ExitWithScope() {}
 
  private:
-  std::vector<Option> saved_llvm_options_;
-
-  /*!
-   * \brief Apply or revert command-line LLVM options
-   * \param apply_otherwise_revert if true, apply the options (saving previous
-   *        values, if false, then restore the saved values
-   * \param dry_run if true, do not make any changes (or save anything)
-   * \return true is changes were made (or would have been made in a dry run),
-   *         false otherwise
-   */
-  bool ApplyLLVMOptions(bool apply_otherwise_revert, bool dry_run = false);
-
   const LLVMInstance& instance_;
   std::weak_ptr<llvm::LLVMContext> ctx_;
 
-  /*!
-   * \brief Global singleton flag indicating whether LLVM's global state has
-   *        been modified or not (via command-line flags). There can only be
-   *        a single such modification in effect at any given time.
-   */
-  static bool modified_llvm_state_;
+  std::string triple_;
+  std::string cpu_;
+  std::vector<std::string> attrs_;
+  llvm::TargetOptions target_options_;
+  llvm::FastMathFlags fast_math_flags_;
+  llvm::CodeGenOpt::Level opt_level_;
+  llvm::Reloc::Model reloc_model_ = llvm::Reloc::PIC_;
+  llvm::CodeModel::Model code_model_ = llvm::CodeModel::Small;
+  std::shared_ptr<llvm::TargetMachine> target_machine_;
 };
 
 }  // namespace codegen
diff --git a/src/target/llvm/llvm_module.cc b/src/target/llvm/llvm_module.cc
index 8749925781..9aed66fffc 100644
--- a/src/target/llvm/llvm_module.cc
+++ b/src/target/llvm/llvm_module.cc
@@ -390,8 +390,8 @@ void LLVMModuleNode::LazyInitJIT() {
 }
 
 bool LLVMModuleNode::IsCompatibleWithHost(const llvm::TargetMachine* tm) const {
-  LLVMTargetInfo host_target(*llvm_instance_, "llvm");
-  auto tm_host = host_target.GetOrCreateTargetMachine();
+  With<LLVMTarget> host_target(*llvm_instance_, "llvm");  // FIXME(kparzysz-quic): nesting
+  auto tm_host = host_target->GetOrCreateTargetMachine();
   if (tm_host->getTargetTriple().getArch() != tm->getTargetTriple().getArch()) {
     LOG(INFO) << "Architecture mismatch: module=" << tm->getTargetTriple().str()
               << " host=" << tm_host->getTargetTriple().str();
@@ -496,7 +496,7 @@ runtime::Module CreateLLVMCppMetadataModule(runtime::metadata::Metadata metadata
   auto llvm_instance = std::make_unique<LLVMInstance>();
   With<LLVMTarget> llvm_target(*llvm_instance, target);
   bool system_lib = runtime->GetAttr<Bool>("system-lib").value_or(Bool(false));
-  auto cg = std::make_unique<CodeGenCPU>();
+  std::unique_ptr<CodeGenCPU> cg{new CodeGenCPU()};
 
   cg->Init("TVMMetadataMod", llvm_target.get(), system_lib, system_lib,
            /*target_c_runtime=*/false);
@@ -544,7 +544,7 @@ runtime::Module CreateLLVMCrtMetadataModule(const Array<runtime::Module>& module
   ICHECK(system_lib && target_c_runtime)
       << "For LLVM C-runtime metadata module, must include --system-lib and --runtime=c; "
       << "got target: " << target->str();
-  auto cg = std::make_unique<CodeGenCPU>();
+  std::unique_ptr<CodeGenCPU> cg{new CodeGenCPU()};
   cg->Init("TVMMetadataMod", llvm_target.operator->(), system_lib, system_lib, target_c_runtime);
 
   cg->DefineFunctionRegistry(func_names);