From 4ac9912f58fd01f8d2d9344894a775b1a0a62a8d Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Mon, 23 Aug 2021 13:29:59 -0500 Subject: [PATCH 1/3] [Hexagon] Remove uses of LLVM from simulator runtime The TVM runtime is not linked with LLVM libraries, so using LLVM in it carries a risk of referencing undefined symbols. This may work for objects defined in header files, but it then relies on LLVM keeping them there. Replace uses of LLVM utilities in the Hexagon simulator runtime, with simple alternatives. --- src/runtime/hexagon/sim/hexagon_device_sim.cc | 175 ++++++++++++++---- 1 file changed, 134 insertions(+), 41 deletions(-) diff --git a/src/runtime/hexagon/sim/hexagon_device_sim.cc b/src/runtime/hexagon/sim/hexagon_device_sim.cc index 1d3f0fd1006f..840d61e3bce4 100644 --- a/src/runtime/hexagon/sim/hexagon_device_sim.cc +++ b/src/runtime/hexagon/sim/hexagon_device_sim.cc @@ -17,12 +17,9 @@ * under the License. */ -#include -#include -#include -#include -#include +#include #include +#include #include #include @@ -31,6 +28,7 @@ #include #include #include +#include #include #include "../hexagon_module.h" @@ -84,6 +82,34 @@ std::unique_ptr make_unique(size_t size) { return std::unique_ptr(new U[size]()); } +// Replacement for llvm::Optional. +template +class Optional { + public: + Optional() : has_value(false) {} + Optional(const T& val) : value(val), has_value(true) {} // NOLINT(*) + Optional(T&& val) : value(val), has_value(true) {} // NOLINT(*) + + bool hasValue() const { return has_value; } + operator bool() const { return hasValue(); } + T operator*() const { + ICHECK(has_value) << "value not set"; + return value; + } + const T* operator->() const { + ICHECK(has_value) << "value not set"; + return &value; + } + T* operator->() { + ICHECK(has_value) << "value not set"; + return &value; + } + + private: + T value; + bool has_value; +}; + // Converter class to translate vector to char**. This relieves the // user from memory reallocation and copying. struct non_const_str { @@ -117,7 +143,7 @@ struct non_const_str { std::vector> storage_; }; -using MaybeString = llvm::Optional; +using MaybeString = Optional; MaybeString front(const string_list& deq) { return !deq.empty() ? MaybeString(deq.front()) : MaybeString(); @@ -130,47 +156,47 @@ MaybeString pop_front(string_list& deq) { // NOLINT(*) return MaybeString(f); } -llvm::Optional to_int(const MaybeString& str) { - auto none = llvm::Optional(); +Optional to_int(const MaybeString& str) { + auto none = Optional(); if (str.hasValue()) { try { size_t pos; int64_t val = std::stoll(*str, &pos, 0); - return pos == str->size() ? llvm::Optional(val) : none; + return pos == str->size() ? Optional(val) : none; } catch (std::invalid_argument) { } } return none; } -llvm::Optional to_uint(const MaybeString& str) { - auto none = llvm::Optional(); +Optional to_uint(const MaybeString& str) { + auto none = Optional(); if (str.hasValue()) { try { size_t pos; uint64_t val = std::stoull(*str, &pos, 0); - return pos == str->size() ? llvm::Optional(val) : none; + return pos == str->size() ? Optional(val) : none; } catch (std::invalid_argument) { } } return none; } -llvm::Optional to_float(const MaybeString& str) { - auto none = llvm::Optional(); +Optional to_float(const MaybeString& str) { + auto none = Optional(); if (str.hasValue()) { try { size_t pos; float val = std::stof(*str, &pos); - return pos == str->size() ? llvm::Optional(val) : none; + return pos == str->size() ? Optional(val) : none; } catch (std::invalid_argument) { } } return none; } -llvm::Optional to_bool(const MaybeString& str) { - auto none = llvm::Optional(); +Optional to_bool(const MaybeString& str) { + auto none = Optional(); if (auto num = to_int(str)) { if (*num == 0) return false; if (*num == 1) return true; @@ -184,9 +210,9 @@ llvm::Optional to_bool(const MaybeString& str) { } template -using MaybeRange = llvm::Optional>; +using MaybeRange = Optional>; -template Parse(const MaybeString&)> +template Parse(const MaybeString&)> MaybeRange to_range(const MaybeString& str) { auto none = MaybeRange(); if (str && !str->empty()) { @@ -202,6 +228,72 @@ MaybeRange to_range(const MaybeString& str) { return none; } +// Replacement for llvm::StringSwitch. +template +class StringSwitch { + public: + explicit StringSwitch(const std::string& key) : key(key) {} + operator T() const { + auto f = map.find(key); + if (f != map.end()) { + return f->second; + } + ICHECK(static_cast(def_val)) << "default value not set"; + return *def_val; + } + StringSwitch& Case(const std::string& key, T val) { + map.insert(std::make_pair(key, val)); + return *this; + } + StringSwitch& Default(T val) { + ICHECK(!static_cast(def_val)) << "default value already set"; + def_val = val; + return *this; + } + + private: + const std::string key; + std::map map; + Optional def_val; +}; + +// Replacement for llvm::sys::fs::access with AccessMode = Execute. +bool FileExists(const std::string& file) { return access(file.c_str(), X_OK) == 0; } + +// Replacement for llvm::sys::Process::FindInEnvPath. +MaybeString FindInEnvPath(const std::string& env_var, const std::string& file) { + auto none = MaybeString(); + if (file.empty() || file[0] == '/') { + return none; + } + + const char* e = getenv(env_var.c_str()); + std::string env_val = e != nullptr ? std::string(e) : std::string(); + + std::vector paths; + // Split the environment variable into individual paths. + size_t first = 0, env_size = env_val.size(); + for (size_t last = 0; last != env_size; ++last) { + if (env_val[last] == ':') { + if (last > first) { + paths.emplace_back(env_val, first, last - first); + } + first = last + 1; + } + } + if (first < env_size) { + paths.emplace_back(env_val, first, env_size - first); + } + + // Search for the file. + for (const std::string& dir : paths) { + std::string full = dir + '/' + file; + if (FileExists(full)) { + return full; + } + } + return none; +} } // namespace detail class HexagonSimulator final : public tvm::runtime::hexagon::Device { @@ -304,17 +396,17 @@ class HexagonSimulator final : public tvm::runtime::hexagon::Device { bool HandleV2PTranslation(string_list& rest); // NOLINT(*) bool HandleVerbose(string_list& rest); // NOLINT(*) - using MaybeUInt64 = llvm::Optional; + using MaybeUInt64 = detail::Optional; using MaybeUIntRange = std::pair; bool should_parse_next(const string_list& rest); - llvm::Optional to_interval(const detail::MaybeString& str); - llvm::Optional to_timingmode(const detail::MaybeString& str); - llvm::Optional to_verbosemode(const detail::MaybeString& str); - llvm::Optional to_nullptr(const detail::MaybeString& str); + detail::Optional to_interval(const detail::MaybeString& str); + detail::Optional to_timingmode(const detail::MaybeString& str); + detail::Optional to_verbosemode(const detail::MaybeString& str); + detail::Optional to_nullptr(const detail::MaybeString& str); MaybeUIntRange ahb_, axi2_; - llvm::Optional debug_port_; + detail::Optional debug_port_; detail::non_const_str sim_dev_args_; using OptionHandler = bool (HexagonSimulator::*)(string_list&); @@ -556,13 +648,13 @@ HexagonSimulator::HexagonSimulator(bool enable_queuing) { LOG(INFO) << "HexagonSimulator: Core version: " << arch_; // Locate the sim_dev binary in PATH, or in the current working directory. - llvm::StringRef sim_dev = "sim_dev"; - detail::MaybeString path_sim_dev = llvm::sys::Process::FindInEnvPath("PATH", sim_dev); + std::string sim_dev = "sim_dev"; + detail::MaybeString path_sim_dev = detail::FindInEnvPath("PATH", sim_dev); if (!path_sim_dev) { - if (!llvm::sys::fs::exists(sim_dev)) { + if (!detail::FileExists(sim_dev)) { LOG(FATAL) << "Cannot find sim_dev in PATH."; } - path_sim_dev = sim_dev.str(); + path_sim_dev = sim_dev; } CHECKED_CALL(ConfigureExecutableBinary, path_sim_dev->c_str()); @@ -1260,8 +1352,8 @@ bool HexagonSimulator::should_parse_next(const string_list& rest) { return false; } -llvm::Optional HexagonSimulator::to_interval(const detail::MaybeString& str) { - auto none = llvm::Optional(); +detail::Optional HexagonSimulator::to_interval(const detail::MaybeString& str) { + auto none = detail::Optional(); if (!str) return none; if (auto val = detail::to_int(*str)) { @@ -1275,7 +1367,7 @@ llvm::Optional HexagonSimulator::to_interval(const detail::Mayb } } - return llvm::StringSwitch>(*str) + return detail::StringSwitch>(*str) .Case("MILLISEC", HEX_MILLISEC) .Case("MICROSEC", HEX_MICROSEC) .Case("NANOSEC", HEX_NANOSEC) @@ -1284,8 +1376,9 @@ llvm::Optional HexagonSimulator::to_interval(const detail::Mayb .Default(none); } -llvm::Optional HexagonSimulator::to_timingmode(const detail::MaybeString& str) { - auto none = llvm::Optional(); +detail::Optional HexagonSimulator::to_timingmode( + const detail::MaybeString& str) { + auto none = detail::Optional(); if (!str) return none; if (auto val = detail::to_int(*str)) { @@ -1298,7 +1391,7 @@ llvm::Optional HexagonSimulator::to_timingmode(const detail:: } } - return llvm::StringSwitch>(*str) + return detail::StringSwitch>(*str) .Case("NOTIMING", HEX_NOTIMING) .Case("TIMING_NODBC", HEX_TIMING_NODBC) .Case("TIMING", HEX_TIMING) @@ -1306,9 +1399,9 @@ llvm::Optional HexagonSimulator::to_timingmode(const detail:: .Default(none); } -llvm::Optional HexagonSimulator::to_verbosemode( +detail::Optional HexagonSimulator::to_verbosemode( const detail::MaybeString& str) { - auto none = llvm::Optional(); + auto none = detail::Optional(); if (!str) return none; if (auto val = detail::to_int(*str)) { @@ -1322,7 +1415,7 @@ llvm::Optional HexagonSimulator::to_verbosemode( } } - return llvm::StringSwitch>(*str) + return detail::StringSwitch>(*str) .Case("SILENT", HEX_SILENT) .Case("QUIET", HEX_QUIET) .Case("NORMAL", HEX_NORMAL) @@ -1331,8 +1424,8 @@ llvm::Optional HexagonSimulator::to_verbosemode( .Default(none); } -llvm::Optional HexagonSimulator::to_nullptr(const detail::MaybeString& str) { - auto none = llvm::Optional(); +detail::Optional HexagonSimulator::to_nullptr(const detail::MaybeString& str) { + auto none = detail::Optional(); if (!str) return none; if (auto val = detail::to_int(*str)) { @@ -1345,7 +1438,7 @@ llvm::Optional HexagonSimulator::to_nullptr(const detail::MaybeS } } - return llvm::StringSwitch>(*str) + return detail::StringSwitch>(*str) .Case("IGNORE", HEX_NULLPTR_IGNORE) .Case("WARN", HEX_NULLPTR_WARN) .Case("FATAL", HEX_NULLPTR_FATAL) From 6b4437e555d8020a28ecb86fa92637afa5b9f9d3 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Mon, 23 Aug 2021 17:08:38 -0500 Subject: [PATCH 2/3] clang-format --- src/runtime/hexagon/sim/hexagon_device_sim.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/hexagon/sim/hexagon_device_sim.cc b/src/runtime/hexagon/sim/hexagon_device_sim.cc index 840d61e3bce4..3d880ed376db 100644 --- a/src/runtime/hexagon/sim/hexagon_device_sim.cc +++ b/src/runtime/hexagon/sim/hexagon_device_sim.cc @@ -87,8 +87,8 @@ template class Optional { public: Optional() : has_value(false) {} - Optional(const T& val) : value(val), has_value(true) {} // NOLINT(*) - Optional(T&& val) : value(val), has_value(true) {} // NOLINT(*) + Optional(const T& val) : value(val), has_value(true) {} // NOLINT(*) + Optional(T&& val) : value(val), has_value(true) {} // NOLINT(*) bool hasValue() const { return has_value; } operator bool() const { return hasValue(); } From 9480e8c7e3802653605af3740760b881167d7b1a Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Mon, 23 Aug 2021 18:23:32 -0500 Subject: [PATCH 3/3] Use dmlc::optional instead of implementing one from scratch Make detail::Optional be derived from dmlc::optional, and add some bits to make it behave more like the C++17's std::optional. The goal is to replace detail::Optional with std::optional, once the project switches to C++17. --- src/runtime/hexagon/sim/hexagon_device_sim.cc | 47 +++++++------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/runtime/hexagon/sim/hexagon_device_sim.cc b/src/runtime/hexagon/sim/hexagon_device_sim.cc index 3d880ed376db..14ab4c30e2f2 100644 --- a/src/runtime/hexagon/sim/hexagon_device_sim.cc +++ b/src/runtime/hexagon/sim/hexagon_device_sim.cc @@ -17,6 +17,7 @@ * under the License. */ +#include #include #include #include @@ -82,32 +83,16 @@ std::unique_ptr make_unique(size_t size) { return std::unique_ptr(new U[size]()); } -// Replacement for llvm::Optional. +// An "Optional" class, originally a replacement for llvm::Optional, then an +// extension of dmlc::optional to make it compatible with C++17's std::optional. template -class Optional { - public: - Optional() : has_value(false) {} - Optional(const T& val) : value(val), has_value(true) {} // NOLINT(*) - Optional(T&& val) : value(val), has_value(true) {} // NOLINT(*) - - bool hasValue() const { return has_value; } - operator bool() const { return hasValue(); } - T operator*() const { - ICHECK(has_value) << "value not set"; - return value; - } - const T* operator->() const { - ICHECK(has_value) << "value not set"; - return &value; - } - T* operator->() { - ICHECK(has_value) << "value not set"; - return &value; - } +struct Optional : public dmlc::optional { + using dmlc::optional::optional; + using dmlc::optional::operator=; + Optional(const T& val) : dmlc::optional(val) {} // NOLINT(*) - private: - T value; - bool has_value; + T* operator->() { return &this->operator*(); } + const T* operator->() const { return &this->operator*(); } }; // Converter class to translate vector to char**. This relieves the @@ -158,7 +143,7 @@ MaybeString pop_front(string_list& deq) { // NOLINT(*) Optional to_int(const MaybeString& str) { auto none = Optional(); - if (str.hasValue()) { + if (str.has_value()) { try { size_t pos; int64_t val = std::stoll(*str, &pos, 0); @@ -171,7 +156,7 @@ Optional to_int(const MaybeString& str) { Optional to_uint(const MaybeString& str) { auto none = Optional(); - if (str.hasValue()) { + if (str.has_value()) { try { size_t pos; uint64_t val = std::stoull(*str, &pos, 0); @@ -184,7 +169,7 @@ Optional to_uint(const MaybeString& str) { Optional to_float(const MaybeString& str) { auto none = Optional(); - if (str.hasValue()) { + if (str.has_value()) { try { size_t pos; float val = std::stof(*str, &pos); @@ -859,19 +844,19 @@ bool HexagonSimulator::Configure(string_list& opts) { } // Check AHB. - if (ahb_.first.hasValue() && ahb_.second.hasValue()) { + if (ahb_.first.has_value() && ahb_.second.has_value()) { CHECKED_CALL(ConfigureAHB, *ahb_.first, *ahb_.second); } else { - ICHECK(!ahb_.first.hasValue() && !ahb_.second.hasValue()) + ICHECK(!ahb_.first.has_value() && !ahb_.second.has_value()) << "HexagonSimulator: please specify both low and high addresses " "for AHB"; } // Check AXI2. - if (axi2_.first.hasValue() && axi2_.second.hasValue()) { + if (axi2_.first.has_value() && axi2_.second.has_value()) { CHECKED_CALL(ConfigureAXI2, *axi2_.first, *axi2_.second); } else { - ICHECK(!axi2_.first.hasValue() && !axi2_.second.hasValue()) + ICHECK(!axi2_.first.has_value() && !axi2_.second.has_value()) << "HexagonSimulator: please specify both low and high addresses " "for AXI2"; }