Use llvm::VersionTuple instead of manual version marshalling

Summary:
This has multiple advantages:
- we need only one function argument/instance variable instead of three
- no need to default initialize variables
- no custom parsing code
- VersionTuple has comparison operators, which makes version comparisons much
  simpler

Reviewers: zturner, friss, clayborg, jingham

Subscribers: emaste, lldb-commits

Differential Revision: https://reviews.llvm.org/D47889

git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@334950 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/lldb/Core/Module.h b/include/lldb/Core/Module.h
index 5212d69..83d5f51 100644
--- a/include/lldb/Core/Module.h
+++ b/include/lldb/Core/Module.h
@@ -703,7 +703,7 @@
   //------------------------------------------------------------------
   virtual void SectionFileAddressesChanged();
 
-  uint32_t GetVersion(uint32_t *versions, uint32_t num_versions);
+  llvm::VersionTuple GetVersion();
 
   //------------------------------------------------------------------
   /// Load an object file from memory.
diff --git a/include/lldb/Host/freebsd/HostInfoFreeBSD.h b/include/lldb/Host/freebsd/HostInfoFreeBSD.h
index 945ec83..5b3a18d 100644
--- a/include/lldb/Host/freebsd/HostInfoFreeBSD.h
+++ b/include/lldb/Host/freebsd/HostInfoFreeBSD.h
@@ -12,12 +12,13 @@
 
 #include "lldb/Host/posix/HostInfoPosix.h"
 #include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
 
 class HostInfoFreeBSD : public HostInfoPosix {
 public:
-  static bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update);
+  static llvm::VersionTuple GetOSVersion();
   static bool GetOSBuildString(std::string &s);
   static bool GetOSKernelDescription(std::string &s);
   static FileSpec GetProgramFileSpec();
diff --git a/include/lldb/Host/linux/HostInfoLinux.h b/include/lldb/Host/linux/HostInfoLinux.h
index d1f2e74..820d3bd 100644
--- a/include/lldb/Host/linux/HostInfoLinux.h
+++ b/include/lldb/Host/linux/HostInfoLinux.h
@@ -12,8 +12,8 @@
 
 #include "lldb/Host/posix/HostInfoPosix.h"
 #include "lldb/Utility/FileSpec.h"
-
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/VersionTuple.h"
 
 #include <string>
 
@@ -30,7 +30,7 @@
 public:
   static void Initialize();
 
-  static bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update);
+  static llvm::VersionTuple GetOSVersion();
   static bool GetOSBuildString(std::string &s);
   static bool GetOSKernelDescription(std::string &s);
   static llvm::StringRef GetDistributionId();
diff --git a/include/lldb/Host/macosx/HostInfoMacOSX.h b/include/lldb/Host/macosx/HostInfoMacOSX.h
index 1f072f6..b8e9688 100644
--- a/include/lldb/Host/macosx/HostInfoMacOSX.h
+++ b/include/lldb/Host/macosx/HostInfoMacOSX.h
@@ -12,6 +12,7 @@
 
 #include "lldb/Host/posix/HostInfoPosix.h"
 #include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
 
@@ -26,7 +27,7 @@
   ~HostInfoMacOSX();
 
 public:
-  static bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update);
+  static llvm::VersionTuple GetOSVersion();
   static bool GetOSBuildString(std::string &s);
   static bool GetOSKernelDescription(std::string &s);
   static FileSpec GetProgramFileSpec();
diff --git a/include/lldb/Host/netbsd/HostInfoNetBSD.h b/include/lldb/Host/netbsd/HostInfoNetBSD.h
index 9ebff6b..0d4de79 100644
--- a/include/lldb/Host/netbsd/HostInfoNetBSD.h
+++ b/include/lldb/Host/netbsd/HostInfoNetBSD.h
@@ -12,12 +12,13 @@
 
 #include "lldb/Host/posix/HostInfoPosix.h"
 #include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
 
 class HostInfoNetBSD : public HostInfoPosix {
 public:
-  static bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update);
+  static llvm::VersionTuple GetOSVersion();
   static bool GetOSBuildString(std::string &s);
   static bool GetOSKernelDescription(std::string &s);
   static FileSpec GetProgramFileSpec();
diff --git a/include/lldb/Host/windows/HostInfoWindows.h b/include/lldb/Host/windows/HostInfoWindows.h
index 9dfbf93..6028ec9 100644
--- a/include/lldb/Host/windows/HostInfoWindows.h
+++ b/include/lldb/Host/windows/HostInfoWindows.h
@@ -12,6 +12,7 @@
 
 #include "lldb/Host/HostInfoBase.h"
 #include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
 
@@ -29,7 +30,7 @@
 
   static size_t GetPageSize();
 
-  static bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update);
+  static llvm::VersionTuple GetOSVersion();
   static bool GetOSBuildString(std::string &s);
   static bool GetOSKernelDescription(std::string &s);
   static bool GetHostname(std::string &s);
diff --git a/include/lldb/Interpreter/OptionGroupPlatform.h b/include/lldb/Interpreter/OptionGroupPlatform.h
index e796624..cda4246 100644
--- a/include/lldb/Interpreter/OptionGroupPlatform.h
+++ b/include/lldb/Interpreter/OptionGroupPlatform.h
@@ -16,6 +16,7 @@
 // Project includes
 #include "lldb/Interpreter/Options.h"
 #include "lldb/Utility/ConstString.h"
+#include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
 
@@ -28,8 +29,6 @@
 public:
   OptionGroupPlatform(bool include_platform_option)
       : OptionGroup(), m_platform_name(), m_sdk_sysroot(),
-        m_os_version_major(UINT32_MAX), m_os_version_minor(UINT32_MAX),
-        m_os_version_update(UINT32_MAX),
         m_include_platform_option(include_platform_option) {}
 
   ~OptionGroupPlatform() override = default;
@@ -72,9 +71,7 @@
   std::string m_platform_name;
   ConstString m_sdk_sysroot;
   ConstString m_sdk_build;
-  uint32_t m_os_version_major;
-  uint32_t m_os_version_minor;
-  uint32_t m_os_version_update;
+  llvm::VersionTuple m_os_version;
   bool m_include_platform_option;
 };
 
diff --git a/include/lldb/Symbol/ObjectFile.h b/include/lldb/Symbol/ObjectFile.h
index 21600d6..84f4ea3 100644
--- a/include/lldb/Symbol/ObjectFile.h
+++ b/include/lldb/Symbol/ObjectFile.h
@@ -20,6 +20,7 @@
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/UUID.h"
 #include "lldb/lldb-private.h"
+#include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
 
@@ -653,45 +654,12 @@
   /// minor and build, but there may be more. This function will extract the
   /// versions from object files if they are available.
   ///
-  /// If \a versions is NULL, or if \a num_versions is 0, the return value
-  /// will indicate how many version numbers are available in this object
-  /// file. Then a subsequent call can be made to this function with a value
-  /// of \a versions and \a num_versions that has enough storage to store some
-  /// or all version numbers.
-  ///
-  /// @param[out] versions
-  ///     A pointer to an array of uint32_t types that is \a num_versions
-  ///     long. If this value is NULL, the return value will indicate
-  ///     how many version numbers are required for a subsequent call
-  ///     to this function so that all versions can be retrieved. If
-  ///     the value is non-NULL, then at most \a num_versions of the
-  ///     existing versions numbers will be filled into \a versions.
-  ///     If there is no version information available, \a versions
-  ///     will be filled with \a num_versions UINT32_MAX values
-  ///     and zero will be returned.
-  ///
-  /// @param[in] num_versions
-  ///     The maximum number of entries to fill into \a versions. If
-  ///     this value is zero, then the return value will indicate
-  ///     how many version numbers there are in total so another call
-  ///     to this function can be make with adequate storage in
-  ///     \a versions to get all of the version numbers. If \a
-  ///     num_versions is less than the actual number of version
-  ///     numbers in this object file, only \a num_versions will be
-  ///     filled into \a versions (if \a versions is non-NULL).
-  ///
   /// @return
-  ///     This function always returns the number of version numbers
-  ///     that this object file has regardless of the number of
-  ///     version numbers that were copied into \a versions.
+  ///     This function returns extracted version numbers as a
+  ///     llvm::VersionTuple. In case of error an empty VersionTuple is
+  ///     returned.
   //------------------------------------------------------------------
-  virtual uint32_t GetVersion(uint32_t *versions, uint32_t num_versions) {
-    if (versions && num_versions) {
-      for (uint32_t i = 0; i < num_versions; ++i)
-        versions[i] = UINT32_MAX;
-    }
-    return 0;
-  }
+  virtual llvm::VersionTuple GetVersion() { return llvm::VersionTuple(); }
 
   //------------------------------------------------------------------
   /// Get the minimum OS version this object file can run on.
@@ -699,45 +667,13 @@
   /// Some object files have information that specifies the minimum OS version
   /// that they can be used on.
   ///
-  /// If \a versions is NULL, or if \a num_versions is 0, the return value
-  /// will indicate how many version numbers are available in this object
-  /// file. Then a subsequent call can be made to this function with a value
-  /// of \a versions and \a num_versions that has enough storage to store some
-  /// or all version numbers.
-  ///
-  /// @param[out] versions
-  ///     A pointer to an array of uint32_t types that is \a num_versions
-  ///     long. If this value is NULL, the return value will indicate
-  ///     how many version numbers are required for a subsequent call
-  ///     to this function so that all versions can be retrieved. If
-  ///     the value is non-NULL, then at most \a num_versions of the
-  ///     existing versions numbers will be filled into \a versions.
-  ///     If there is no version information available, \a versions
-  ///     will be filled with \a num_versions UINT32_MAX values
-  ///     and zero will be returned.
-  ///
-  /// @param[in] num_versions
-  ///     The maximum number of entries to fill into \a versions. If
-  ///     this value is zero, then the return value will indicate
-  ///     how many version numbers there are in total so another call
-  ///     to this function can be make with adequate storage in
-  ///     \a versions to get all of the version numbers. If \a
-  ///     num_versions is less than the actual number of version
-  ///     numbers in this object file, only \a num_versions will be
-  ///     filled into \a versions (if \a versions is non-NULL).
-  ///
   /// @return
-  ///     This function always returns the number of version numbers
-  ///     that this object file has regardless of the number of
-  ///     version numbers that were copied into \a versions.
+  ///     This function returns extracted version numbers as a
+  ///     llvm::VersionTuple. In case of error an empty VersionTuple is
+  ///     returned.
   //------------------------------------------------------------------
-  virtual uint32_t GetMinimumOSVersion(uint32_t *versions,
-                                       uint32_t num_versions) {
-    if (versions && num_versions) {
-      for (uint32_t i = 0; i < num_versions; ++i)
-        versions[i] = UINT32_MAX;
-    }
-    return 0;
+  virtual llvm::VersionTuple GetMinimumOSVersion() {
+    return llvm::VersionTuple();
   }
 
   //------------------------------------------------------------------
diff --git a/include/lldb/Target/Platform.h b/include/lldb/Target/Platform.h
index e0ae657..047196e 100644
--- a/include/lldb/Target/Platform.h
+++ b/include/lldb/Target/Platform.h
@@ -30,6 +30,7 @@
 #include "lldb/Utility/Timeout.h"
 #include "lldb/lldb-private-forward.h"
 #include "lldb/lldb-public.h"
+#include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
 
@@ -225,10 +226,9 @@
   /// simulator might be simulating a different OS. The \a process parameter
   /// might be specified to help to determine the OS version.
   //------------------------------------------------------------------
-  virtual bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update,
-                            Process *process = nullptr);
+  virtual llvm::VersionTuple GetOSVersion(Process *process = nullptr);
 
-  bool SetOSVersion(uint32_t major, uint32_t minor, uint32_t update);
+  bool SetOSVersion(llvm::VersionTuple os_version);
 
   bool GetOSBuildString(std::string &s);
 
@@ -874,9 +874,7 @@
                           // modules that have no install path set
   std::string m_remote_url;
   std::string m_name;
-  uint32_t m_major_os_version;
-  uint32_t m_minor_os_version;
-  uint32_t m_update_os_version;
+  llvm::VersionTuple m_os_version;
   ArchSpec
       m_system_arch; // The architecture of the kernel or the remote platform
   typedef std::map<uint32_t, ConstString> IDToNameMap;
diff --git a/include/lldb/Target/Process.h b/include/lldb/Target/Process.h
index 9766984..71ea364 100644
--- a/include/lldb/Target/Process.h
+++ b/include/lldb/Target/Process.h
@@ -54,6 +54,7 @@
 #include "lldb/lldb-private.h"
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
 
@@ -1447,26 +1448,11 @@
   /// platform that might itself be running natively, but have different
   /// heuristics for figuring out which OS is is emulating.
   ///
-  /// @param[out] major
-  ///    The major OS version, or UINT32_MAX if it can't be determined
-  ///
-  /// @param[out] minor
-  ///    The minor OS version, or UINT32_MAX if it can't be determined
-  ///
-  /// @param[out] update
-  ///    The update OS version, or UINT32_MAX if it can't be determined
-  ///
   /// @return
-  ///     Returns \b true if the host OS version info was filled in
-  ///     and \b false otherwise.
+  ///     Returns the version tuple of the host OS. In case of failure an empty
+  ///     VersionTuple is returner.
   //------------------------------------------------------------------
-  virtual bool GetHostOSVersion(uint32_t &major, uint32_t &minor,
-                                uint32_t &update) {
-    major = UINT32_MAX;
-    minor = UINT32_MAX;
-    update = UINT32_MAX;
-    return false;
-  }
+  virtual llvm::VersionTuple GetHostOSVersion() { return llvm::VersionTuple(); }
 
   //------------------------------------------------------------------
   /// Get the target object pointer for this module.
diff --git a/include/lldb/Utility/Args.h b/include/lldb/Utility/Args.h
index 18344f0..cb83152 100644
--- a/include/lldb/Utility/Args.h
+++ b/include/lldb/Utility/Args.h
@@ -324,9 +324,6 @@
 
   static uint32_t StringToGenericRegister(llvm::StringRef s);
 
-  static bool StringToVersion(llvm::StringRef string, uint32_t &major,
-                              uint32_t &minor, uint32_t &update);
-
   static const char *GetShellSafeArgument(const FileSpec &shell,
                                           const char *unsafe_arg,
                                           std::string &safe_arg);
diff --git a/source/API/SBModule.cpp b/source/API/SBModule.cpp
index 1de1e54..8866cf4 100644
--- a/source/API/SBModule.cpp
+++ b/source/API/SBModule.cpp
@@ -531,16 +531,29 @@
 }
 
 uint32_t SBModule::GetVersion(uint32_t *versions, uint32_t num_versions) {
-  ModuleSP module_sp(GetSP());
-  if (module_sp)
-    return module_sp->GetVersion(versions, num_versions);
-  else {
-    if (versions && num_versions) {
-      for (uint32_t i = 0; i < num_versions; ++i)
-        versions[i] = UINT32_MAX;
-    }
-    return 0;
-  }
+  llvm::VersionTuple version;
+  if (ModuleSP module_sp = GetSP())
+    version = module_sp->GetVersion();
+  uint32_t result = 0;
+  if (!version.empty())
+    ++result;
+  if (version.getMinor())
+    ++result;
+  if(version.getSubminor())
+    ++result;
+
+  if (!versions)
+    return result;
+
+  if (num_versions > 0)
+    versions[0] = version.empty() ? UINT32_MAX : version.getMajor();
+  if (num_versions > 1)
+    versions[1] = version.getMinor().getValueOr(UINT32_MAX);
+  if (num_versions > 2)
+    versions[2] = version.getSubminor().getValueOr(UINT32_MAX);
+  for (uint32_t i = 3; i < num_versions; ++i)
+    versions[i] = UINT32_MAX;
+  return result;
 }
 
 lldb::SBFileSpec SBModule::GetSymbolFileSpec() const {
diff --git a/source/API/SBPlatform.cpp b/source/API/SBPlatform.cpp
index d559a66..5f29f00 100644
--- a/source/API/SBPlatform.cpp
+++ b/source/API/SBPlatform.cpp
@@ -330,27 +330,24 @@
 }
 
 uint32_t SBPlatform::GetOSMajorVersion() {
-  uint32_t major, minor, update;
-  PlatformSP platform_sp(GetSP());
-  if (platform_sp && platform_sp->GetOSVersion(major, minor, update))
-    return major;
-  return UINT32_MAX;
+  llvm::VersionTuple version;
+  if (PlatformSP platform_sp = GetSP())
+    version = platform_sp->GetOSVersion();
+  return version.empty() ? UINT32_MAX : version.getMajor();
 }
 
 uint32_t SBPlatform::GetOSMinorVersion() {
-  uint32_t major, minor, update;
-  PlatformSP platform_sp(GetSP());
-  if (platform_sp && platform_sp->GetOSVersion(major, minor, update))
-    return minor;
-  return UINT32_MAX;
+  llvm::VersionTuple version;
+  if (PlatformSP platform_sp = GetSP())
+    version = platform_sp->GetOSVersion();
+  return version.getMinor().getValueOr(UINT32_MAX);
 }
 
 uint32_t SBPlatform::GetOSUpdateVersion() {
-  uint32_t major, minor, update;
-  PlatformSP platform_sp(GetSP());
-  if (platform_sp && platform_sp->GetOSVersion(major, minor, update))
-    return update;
-  return UINT32_MAX;
+  llvm::VersionTuple version;
+  if (PlatformSP platform_sp = GetSP())
+    version = platform_sp->GetOSVersion();
+  return version.getSubminor().getValueOr(UINT32_MAX);
 }
 
 SBError SBPlatform::Get(SBFileSpec &src, SBFileSpec &dst) {
diff --git a/source/Core/Module.cpp b/source/Core/Module.cpp
index 6ffcd78..3b1a4fd 100644
--- a/source/Core/Module.cpp
+++ b/source/Core/Module.cpp
@@ -1640,16 +1640,10 @@
   return m_source_mappings.RemapPath(path, new_path);
 }
 
-uint32_t Module::GetVersion(uint32_t *versions, uint32_t num_versions) {
-  ObjectFile *obj_file = GetObjectFile();
-  if (obj_file)
-    return obj_file->GetVersion(versions, num_versions);
-
-  if (versions != nullptr && num_versions != 0) {
-    for (uint32_t i = 0; i < num_versions; ++i)
-      versions[i] = LLDB_INVALID_MODULE_VERSION;
-  }
-  return 0;
+llvm::VersionTuple Module::GetVersion() {
+  if (ObjectFile *obj_file = GetObjectFile())
+    return obj_file->GetVersion();
+  return llvm::VersionTuple();
 }
 
 bool Module::GetIsDynamicLinkEditor() {
diff --git a/source/Host/freebsd/HostInfoFreeBSD.cpp b/source/Host/freebsd/HostInfoFreeBSD.cpp
index 4f79f62..8013114 100644
--- a/source/Host/freebsd/HostInfoFreeBSD.cpp
+++ b/source/Host/freebsd/HostInfoFreeBSD.cpp
@@ -18,16 +18,17 @@
 
 using namespace lldb_private;
 
-bool HostInfoFreeBSD::GetOSVersion(uint32_t &major, uint32_t &minor,
-                                   uint32_t &update) {
+llvm::VersionTuple HostInfoFreeBSD::GetOSVersion() {
   struct utsname un;
 
   ::memset(&un, 0, sizeof(utsname));
   if (uname(&un) < 0)
     return false;
 
-  int status = sscanf(un.release, "%u.%u", &major, &minor);
-  return status == 2;
+  unsigned major, minor;
+  if (2 == sscanf(un.release, "%u.%u", &major, &minor))
+    return llvm::VersionTuple(major, minor);
+  return llvm::VersionTuple();
 }
 
 bool HostInfoFreeBSD::GetOSBuildString(std::string &s) {
@@ -73,4 +74,4 @@
     }
   }
   return g_program_filespec;
-}
\ No newline at end of file
+}
diff --git a/source/Host/linux/HostInfoLinux.cpp b/source/Host/linux/HostInfoLinux.cpp
index 4983947..1d95010 100644
--- a/source/Host/linux/HostInfoLinux.cpp
+++ b/source/Host/linux/HostInfoLinux.cpp
@@ -26,12 +26,8 @@
 
 namespace {
 struct HostInfoLinuxFields {
-  HostInfoLinuxFields() : m_os_major(0), m_os_minor(0), m_os_update(0) {}
-
   std::string m_distribution_id;
-  uint32_t m_os_major;
-  uint32_t m_os_minor;
-  uint32_t m_os_update;
+  llvm::VersionTuple m_os_version;
 };
 
 HostInfoLinuxFields *g_fields = nullptr;
@@ -43,34 +39,21 @@
   g_fields = new HostInfoLinuxFields();
 }
 
-bool HostInfoLinux::GetOSVersion(uint32_t &major, uint32_t &minor,
-                                 uint32_t &update) {
-  static bool success = false;
+llvm::VersionTuple HostInfoLinux::GetOSVersion() {
   static llvm::once_flag g_once_flag;
   llvm::call_once(g_once_flag, []() {
-
     struct utsname un;
-    if (uname(&un) == 0) {
-      int status = sscanf(un.release, "%u.%u.%u", &g_fields->m_os_major,
-                          &g_fields->m_os_minor, &g_fields->m_os_update);
-      if (status == 3)
-        success = true;
-      else {
-        // Some kernels omit the update version, so try looking for just "X.Y"
-        // and set update to 0.
-        g_fields->m_os_update = 0;
-        status = sscanf(un.release, "%u.%u", &g_fields->m_os_major,
-                        &g_fields->m_os_minor);
-        if (status == 2)
-          success = true;
-      }
-    }
+    if (uname(&un) != 0)
+      return;
+
+    llvm::StringRef release = un.release;
+    // The kernel release string can include a lot of stuff (e.g.
+    // 4.9.0-6-amd64). We're only interested in the numbered prefix.
+    release = release.substr(0, release.find_first_not_of("0123456789."));
+    g_fields->m_os_version.tryParse(release);
   });
 
-  major = g_fields->m_os_major;
-  minor = g_fields->m_os_minor;
-  update = g_fields->m_os_update;
-  return success;
+  return g_fields->m_os_version;
 }
 
 bool HostInfoLinux::GetOSBuildString(std::string &s) {
diff --git a/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/source/Host/macosx/objcxx/HostInfoMacOSX.mm
index dbb7482..5ec57c9 100644
--- a/source/Host/macosx/objcxx/HostInfoMacOSX.mm
+++ b/source/Host/macosx/objcxx/HostInfoMacOSX.mm
@@ -76,32 +76,21 @@
   return false;
 }
 
-bool HostInfoMacOSX::GetOSVersion(uint32_t &major, uint32_t &minor,
-                                  uint32_t &update) {
-  static uint32_t g_major = 0;
-  static uint32_t g_minor = 0;
-  static uint32_t g_update = 0;
+llvm::VersionTuple HostInfoMacOSX::GetOSVersion() {
+  static llvm::VersionTuple g_version;
 
-  if (g_major == 0) {
+  if (g_version.empty()) {
     @autoreleasepool {
       NSDictionary *version_info = [NSDictionary
           dictionaryWithContentsOfFile:
               @"/System/Library/CoreServices/SystemVersion.plist"];
       NSString *version_value = [version_info objectForKey:@"ProductVersion"];
       const char *version_str = [version_value UTF8String];
-      if (version_str)
-        Args::StringToVersion(llvm::StringRef(version_str), g_major, g_minor,
-                              g_update);
+      g_version.tryParse(version_str);
     }
   }
 
-  if (g_major != 0) {
-    major = g_major;
-    minor = g_minor;
-    update = g_update;
-    return true;
-  }
-  return false;
+  return g_version;
 }
 
 FileSpec HostInfoMacOSX::GetProgramFileSpec() {
diff --git a/source/Host/netbsd/HostInfoNetBSD.cpp b/source/Host/netbsd/HostInfoNetBSD.cpp
index 11ac115..19afae3 100644
--- a/source/Host/netbsd/HostInfoNetBSD.cpp
+++ b/source/Host/netbsd/HostInfoNetBSD.cpp
@@ -21,8 +21,7 @@
 
 using namespace lldb_private;
 
-bool HostInfoNetBSD::GetOSVersion(uint32_t &major, uint32_t &minor,
-                                  uint32_t &update) {
+llvm::VersionTuple HostInfoNetBSD::GetOSVersion() {
   struct utsname un;
 
   ::memset(&un, 0, sizeof(un));
@@ -33,18 +32,14 @@
   int status = ::sscanf(un.release, "%" PRIu32 ".%" PRIu32 ".%" PRIu32, &major,
                         &minor, &update);
   switch (status) {
-  case 0:
-    return false;
   case 1:
-    minor = 0;
-  /* FALLTHROUGH */
+    return llvm::VersionTuple(major);
   case 2:
-    update = 0;
-  /* FALLTHROUGH */
+    return llvm::VersionTuple(major, minor);
   case 3:
-  default:
-    return true;
+    return llvm::VersionTuple(major, minor, update);
   }
+  return llvm::VersionTuple();
 }
 
 bool HostInfoNetBSD::GetOSBuildString(std::string &s) {
diff --git a/source/Host/windows/HostInfoWindows.cpp b/source/Host/windows/HostInfoWindows.cpp
index 5f074c8..a6e16d1 100644
--- a/source/Host/windows/HostInfoWindows.cpp
+++ b/source/Host/windows/HostInfoWindows.cpp
@@ -42,8 +42,7 @@
   return systemInfo.dwPageSize;
 }
 
-bool HostInfoWindows::GetOSVersion(uint32_t &major, uint32_t &minor,
-                                   uint32_t &update) {
+llvm::VersionTuple HostInfoWindows::GetOSVersion() {
   OSVERSIONINFOEX info;
 
   ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
@@ -54,16 +53,12 @@
   // in favor of the new Windows Version Helper APIs.  Since we don't specify a
   // minimum SDK version, it's easier to simply disable the warning rather than
   // try to support both APIs.
-  if (GetVersionEx((LPOSVERSIONINFO)&info) == 0) {
-    return false;
-  }
+  if (GetVersionEx((LPOSVERSIONINFO)&info) == 0)
+    return llvm::VersionTuple();
 #pragma warning(pop)
 
-  major = info.dwMajorVersion;
-  minor = info.dwMinorVersion;
-  update = info.wServicePackMajor;
-
-  return true;
+  return llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion,
+                            info.wServicePackMajor);
 }
 
 bool HostInfoWindows::GetOSBuildString(std::string &s) {
diff --git a/source/Interpreter/OptionGroupPlatform.cpp b/source/Interpreter/OptionGroupPlatform.cpp
index 5747c6a..4797427 100644
--- a/source/Interpreter/OptionGroupPlatform.cpp
+++ b/source/Interpreter/OptionGroupPlatform.cpp
@@ -44,10 +44,8 @@
   if (platform_sp) {
     interpreter.GetDebugger().GetPlatformList().Append(platform_sp,
                                                        make_selected);
-    if (m_os_version_major != UINT32_MAX) {
-      platform_sp->SetOSVersion(m_os_version_major, m_os_version_minor,
-                                m_os_version_update);
-    }
+    if (!m_os_version.empty())
+      platform_sp->SetOSVersion(m_os_version);
 
     if (m_sdk_sysroot)
       platform_sp->SetSDKRootDirectory(m_sdk_sysroot);
@@ -64,9 +62,7 @@
   m_platform_name.clear();
   m_sdk_sysroot.Clear();
   m_sdk_build.Clear();
-  m_os_version_major = UINT32_MAX;
-  m_os_version_minor = UINT32_MAX;
-  m_os_version_update = UINT32_MAX;
+  m_os_version = llvm::VersionTuple();
 }
 
 static OptionDefinition g_option_table[] = {
@@ -108,10 +104,9 @@
     break;
 
   case 'v':
-    if (!Args::StringToVersion(option_arg, m_os_version_major,
-                               m_os_version_minor, m_os_version_update))
-      error.SetErrorStringWithFormat("invalid version string '%s'",
-                                     option_arg.str().c_str());
+    if (m_os_version.tryParse(option_arg))
+      error.SetErrorStringWithFormatv("invalid version string '{0}'",
+                                      option_arg);
     break;
 
   case 'b':
@@ -143,17 +138,8 @@
     if (m_sdk_sysroot && m_sdk_sysroot != platform_sp->GetSDKRootDirectory())
       return false;
 
-    if (m_os_version_major != UINT32_MAX) {
-      uint32_t major, minor, update;
-      if (platform_sp->GetOSVersion(major, minor, update)) {
-        if (m_os_version_major != major)
-          return false;
-        if (m_os_version_minor != minor)
-          return false;
-        if (m_os_version_update != update)
-          return false;
-      }
-    }
+    if (!m_os_version.empty() && m_os_version != platform_sp->GetOSVersion())
+      return false;
     return true;
   }
   return false;
diff --git a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
index 96242fd..c35905b 100644
--- a/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
+++ b/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
@@ -1122,34 +1122,29 @@
 
 bool DynamicLoaderDarwin::UseDYLDSPI(Process *process) {
   Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
-  uint32_t major, minor, update;
-
   bool use_new_spi_interface = false;
 
-  if (process->GetHostOSVersion(major, minor, update)) {
+  llvm::VersionTuple version = process->GetHostOSVersion();
+  if (!version.empty()) {
     const llvm::Triple::OSType os_type =
         process->GetTarget().GetArchitecture().GetTriple().getOS();
 
     // macOS 10.12 and newer
     if (os_type == llvm::Triple::MacOSX &&
-        (major > 10 || (major == 10 && minor >= 12))) {
+        version >= llvm::VersionTuple(10, 12))
       use_new_spi_interface = true;
-    }
 
     // iOS 10 and newer
-    if (os_type == llvm::Triple::IOS && major >= 10) {
+    if (os_type == llvm::Triple::IOS && version >= llvm::VersionTuple(10))
       use_new_spi_interface = true;
-    }
 
     // tvOS 10 and newer
-    if (os_type == llvm::Triple::TvOS && major >= 10) {
+    if (os_type == llvm::Triple::TvOS && version >= llvm::VersionTuple(10))
       use_new_spi_interface = true;
-    }
 
     // watchOS 3 and newer
-    if (os_type == llvm::Triple::WatchOS && major >= 3) {
+    if (os_type == llvm::Triple::WatchOS && version >= llvm::VersionTuple(3))
       use_new_spi_interface = true;
-    }
   }
 
   if (log) {
diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
index 68df433..b1513b5 100644
--- a/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
+++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
@@ -454,9 +454,8 @@
 static bool isLoadBiasIncorrect(Target &target, const std::string &file_path) {
   // On Android L (API 21, 22) the load address of the "/system/bin/linker"
   // isn't filled in correctly.
-  uint32_t os_major = 0, os_minor = 0, os_update = 0;
+  unsigned os_major = target.GetPlatform()->GetOSVersion().getMajor();
   if (target.GetArchitecture().GetTriple().isAndroid() &&
-      target.GetPlatform()->GetOSVersion(os_major, os_minor, os_update) &&
       (os_major == 21 || os_major == 22) &&
       (file_path == "/system/bin/linker" ||
        file_path == "/system/bin/linker64")) {
diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
index 5516cf9..fef42c7 100644
--- a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
+++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
@@ -293,16 +293,14 @@
 uint32_t AppleObjCRuntime::GetFoundationVersion() {
   if (!m_Foundation_major.hasValue()) {
     const ModuleList &modules = m_process->GetTarget().GetImages();
-    uint32_t major = UINT32_MAX;
     for (uint32_t idx = 0; idx < modules.GetSize(); idx++) {
       lldb::ModuleSP module_sp = modules.GetModuleAtIndex(idx);
       if (!module_sp)
         continue;
       if (strcmp(module_sp->GetFileSpec().GetFilename().AsCString(""),
                  "Foundation") == 0) {
-        module_sp->GetVersion(&major, 1);
-        m_Foundation_major = major;
-        return major;
+        m_Foundation_major = module_sp->GetVersion().getMajor();
+        return *m_Foundation_major;
       }
     }
     return LLDB_INVALID_MODULE_VERSION;
diff --git a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index ce62077..0aa0fc9 100644
--- a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -5549,8 +5549,7 @@
   return eStrataUnknown;
 }
 
-uint32_t ObjectFileMachO::GetVersion(uint32_t *versions,
-                                     uint32_t num_versions) {
+llvm::VersionTuple ObjectFileMachO::GetVersion() {
   ModuleSP module_sp(GetModule());
   if (module_sp) {
     std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
@@ -5578,23 +5577,13 @@
     }
 
     if (version_cmd == LC_ID_DYLIB) {
-      if (versions != NULL && num_versions > 0) {
-        if (num_versions > 0)
-          versions[0] = (version & 0xFFFF0000ull) >> 16;
-        if (num_versions > 1)
-          versions[1] = (version & 0x0000FF00ull) >> 8;
-        if (num_versions > 2)
-          versions[2] = (version & 0x000000FFull);
-        // Fill in an remaining version numbers with invalid values
-        for (i = 3; i < num_versions; ++i)
-          versions[i] = UINT32_MAX;
-      }
-      // The LC_ID_DYLIB load command has a version with 3 version numbers in
-      // it, so always return 3
-      return 3;
+      unsigned major = (version & 0xFFFF0000ull) >> 16;
+      unsigned minor = (version & 0x0000FF00ull) >> 8;
+      unsigned subminor = (version & 0x000000FFull);
+      return llvm::VersionTuple(major, minor, subminor);
     }
   }
-  return false;
+  return llvm::VersionTuple();
 }
 
 bool ObjectFileMachO::GetArchitecture(ArchSpec &arch) {
@@ -5708,12 +5697,10 @@
 #endif
 }
 
-uint32_t ObjectFileMachO::GetMinimumOSVersion(uint32_t *versions,
-                                              uint32_t num_versions) {
-  if (m_min_os_versions.empty()) {
+llvm::VersionTuple ObjectFileMachO::GetMinimumOSVersion() {
+  if (!m_min_os_version) {
     lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
-    bool success = false;
-    for (uint32_t i = 0; success == false && i < m_header.ncmds; ++i) {
+    for (uint32_t i = 0; i < m_header.ncmds; ++i) {
       const lldb::offset_t load_cmd_offset = offset;
 
       version_min_command lc;
@@ -5729,35 +5716,21 @@
           const uint32_t yy = (lc.version >> 8) & 0xffu;
           const uint32_t zz = lc.version & 0xffu;
           if (xxxx) {
-            m_min_os_versions.push_back(xxxx);
-            m_min_os_versions.push_back(yy);
-            m_min_os_versions.push_back(zz);
+            m_min_os_version = llvm::VersionTuple(xxxx, yy, zz);
+            break;
           }
-          success = true;
         }
       }
       offset = load_cmd_offset + lc.cmdsize;
     }
 
-    if (success == false) {
-      // Push an invalid value so we don't keep trying to
-      m_min_os_versions.push_back(UINT32_MAX);
+    if (!m_min_os_version) {
+      // Set version to an empty value so we don't keep trying to
+      m_min_os_version = llvm::VersionTuple();
     }
   }
 
-  if (m_min_os_versions.size() > 1 || m_min_os_versions[0] != UINT32_MAX) {
-    if (versions != NULL && num_versions > 0) {
-      for (size_t i = 0; i < num_versions; ++i) {
-        if (i < m_min_os_versions.size())
-          versions[i] = m_min_os_versions[i];
-        else
-          versions[i] = 0;
-      }
-    }
-    return m_min_os_versions.size();
-  }
-  // Call the superclasses version that will empty out the data
-  return ObjectFile::GetMinimumOSVersion(versions, num_versions);
+  return *m_min_os_version;
 }
 
 uint32_t ObjectFileMachO::GetSDKVersion(uint32_t *versions,
diff --git a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
index ca64d88..8cdd80f 100644
--- a/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
+++ b/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h
@@ -123,10 +123,9 @@
 
   ObjectFile::Strata CalculateStrata() override;
 
-  uint32_t GetVersion(uint32_t *versions, uint32_t num_versions) override;
+  llvm::VersionTuple GetVersion() override;
 
-  uint32_t GetMinimumOSVersion(uint32_t *versions,
-                               uint32_t num_versions) override;
+  llvm::VersionTuple GetMinimumOSVersion() override;
 
   uint32_t GetSDKVersion(uint32_t *versions, uint32_t num_versions) override;
 
@@ -209,7 +208,7 @@
   llvm::MachO::dysymtab_command m_dysymtab;
   std::vector<llvm::MachO::segment_command_64> m_mach_segments;
   std::vector<llvm::MachO::section_64> m_mach_sections;
-  std::vector<uint32_t> m_min_os_versions;
+  llvm::Optional<llvm::VersionTuple> m_min_os_version;
   std::vector<uint32_t> m_sdk_versions;
   typedef lldb_private::RangeVector<uint32_t, uint32_t> FileRangeArray;
   lldb_private::Address m_entry_point_address;
diff --git a/source/Plugins/Platform/Android/PlatformAndroid.cpp b/source/Plugins/Platform/Android/PlatformAndroid.cpp
index b7fd506..1cedcde 100644
--- a/source/Plugins/Platform/Android/PlatformAndroid.cpp
+++ b/source/Plugins/Platform/Android/PlatformAndroid.cpp
@@ -360,10 +360,8 @@
 }
 
 bool PlatformAndroid::GetRemoteOSVersion() {
-  m_major_os_version = GetSdkVersion();
-  m_minor_os_version = 0;
-  m_update_os_version = 0;
-  return m_major_os_version != 0;
+  m_os_version = llvm::VersionTuple(GetSdkVersion());
+  return !m_os_version.empty();
 }
 
 llvm::StringRef
diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index 9e2b721..10da80f 100644
--- a/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -1382,18 +1382,14 @@
   return g_xcode_filespec;
 }
 
-bool PlatformDarwin::SDKSupportsModules(SDKType sdk_type, uint32_t major,
-                                        uint32_t minor, uint32_t micro) {
+bool PlatformDarwin::SDKSupportsModules(SDKType sdk_type,
+                                        llvm::VersionTuple version) {
   switch (sdk_type) {
   case SDKType::MacOSX:
-    if (major > 10 || (major == 10 && minor >= 10))
-      return true;
-    break;
+    return version >= llvm::VersionTuple(10, 10);
   case SDKType::iPhoneOS:
   case SDKType::iPhoneSimulator:
-    if (major >= 8)
-      return true;
-    break;
+    return version >= llvm::VersionTuple(8);
   }
 
   return false;
@@ -1415,32 +1411,10 @@
       return false;
     }
 
-    const size_t major_dot_offset = version_part.find('.');
-    if (major_dot_offset == llvm::StringRef::npos)
+    llvm::VersionTuple version;
+    if (version.tryParse(version_part))
       return false;
-
-    const llvm::StringRef major_version =
-        version_part.slice(0, major_dot_offset);
-    const llvm::StringRef minor_part =
-        version_part.drop_front(major_dot_offset + 1);
-
-    const size_t minor_dot_offset = minor_part.find('.');
-    if (minor_dot_offset == llvm::StringRef::npos)
-      return false;
-
-    const llvm::StringRef minor_version = minor_part.slice(0, minor_dot_offset);
-
-    unsigned int major = 0;
-    unsigned int minor = 0;
-    unsigned int micro = 0;
-
-    if (major_version.getAsInteger(10, major))
-      return false;
-
-    if (minor_version.getAsInteger(10, minor))
-      return false;
-
-    return SDKSupportsModules(desired_type, major, minor, micro);
+    return SDKSupportsModules(desired_type, version);
   }
 
   return false;
@@ -1512,18 +1486,17 @@
   sdks_spec.AppendPathComponent("SDKs");
 
   if (sdk_type == SDKType::MacOSX) {
-    uint32_t major = 0;
-    uint32_t minor = 0;
-    uint32_t micro = 0;
+    llvm::VersionTuple version = HostInfo::GetOSVersion();
 
-    if (HostInfo::GetOSVersion(major, minor, micro)) {
-      if (SDKSupportsModules(SDKType::MacOSX, major, minor, micro)) {
+    if (!version.empty()) {
+      if (SDKSupportsModules(SDKType::MacOSX, version)) {
         // We slightly prefer the exact SDK for this machine.  See if it is
         // there.
 
         FileSpec native_sdk_spec = sdks_spec;
         StreamString native_sdk_name;
-        native_sdk_name.Printf("MacOSX%u.%u.sdk", major, minor);
+        native_sdk_name.Printf("MacOSX%u.%u.sdk", version.getMajor(),
+                               version.getMinor().getValueOr(0));
         native_sdk_spec.AppendPathComponent(native_sdk_name.GetString());
 
         if (native_sdk_spec.Exists()) {
@@ -1536,14 +1509,14 @@
   return FindSDKInXcodeForModules(sdk_type, sdks_spec);
 }
 
-std::tuple<uint32_t, uint32_t, uint32_t, llvm::StringRef>
+std::tuple<llvm::VersionTuple, llvm::StringRef>
 PlatformDarwin::ParseVersionBuildDir(llvm::StringRef dir) {
-  uint32_t major, minor, update;
   llvm::StringRef build;
   llvm::StringRef version_str;
   llvm::StringRef build_str;
   std::tie(version_str, build_str) = dir.split(' ');
-  if (Args::StringToVersion(version_str, major, minor, update) ||
+  llvm::VersionTuple version;
+  if (!version.tryParse(version_str) ||
       build_str.empty()) {
     if (build_str.consume_front("(")) {
       size_t pos = build_str.find(')');
@@ -1551,7 +1524,7 @@
     }
   }
 
-  return std::make_tuple(major, minor, update, build);
+  return std::make_tuple(version, build);
 }
 
 void PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(
@@ -1563,7 +1536,6 @@
   options.insert(options.end(), apple_arguments.begin(), apple_arguments.end());
 
   StreamString minimum_version_option;
-  uint32_t versions[3] = {0, 0, 0};
   bool use_current_os_version = false;
   switch (sdk_type) {
   case SDKType::iPhoneOS:
@@ -1587,9 +1559,9 @@
     break;
   }
 
-  bool versions_valid = false;
+  llvm::VersionTuple version;
   if (use_current_os_version)
-    versions_valid = GetOSVersion(versions[0], versions[1], versions[2]);
+    version = GetOSVersion();
   else if (target) {
     // Our OS doesn't match our executable so we need to get the min OS version
     // from the object file
@@ -1597,35 +1569,23 @@
     if (exe_module_sp) {
       ObjectFile *object_file = exe_module_sp->GetObjectFile();
       if (object_file)
-        versions_valid = object_file->GetMinimumOSVersion(versions, 3) > 0;
+        version = object_file->GetMinimumOSVersion();
     }
   }
   // Only add the version-min options if we got a version from somewhere
-  if (versions_valid && versions[0] != UINT32_MAX) {
-    // Make any invalid versions be zero if needed
-    if (versions[1] == UINT32_MAX)
-      versions[1] = 0;
-    if (versions[2] == UINT32_MAX)
-      versions[2] = 0;
-
+  if (!version.empty()) {
     switch (sdk_type) {
     case SDKType::iPhoneOS:
       minimum_version_option.PutCString("-mios-version-min=");
-      minimum_version_option.PutCString(
-          llvm::VersionTuple(versions[0], versions[1], versions[2])
-              .getAsString());
+      minimum_version_option.PutCString(version.getAsString());
       break;
     case SDKType::iPhoneSimulator:
       minimum_version_option.PutCString("-mios-simulator-version-min=");
-      minimum_version_option.PutCString(
-          llvm::VersionTuple(versions[0], versions[1], versions[2])
-              .getAsString());
+      minimum_version_option.PutCString(version.getAsString());
       break;
     case SDKType::MacOSX:
       minimum_version_option.PutCString("-mmacosx-version-min=");
-      minimum_version_option.PutCString(
-          llvm::VersionTuple(versions[0], versions[1], versions[2])
-              .getAsString());
+      minimum_version_option.PutCString(version.getAsString());
     }
     options.push_back(minimum_version_option.GetString());
   }
@@ -1652,16 +1612,15 @@
   return ConstString(stream.GetString());
 }
 
-bool PlatformDarwin::GetOSVersion(uint32_t &major, uint32_t &minor,
-                                  uint32_t &update, Process *process) {
+llvm::VersionTuple PlatformDarwin::GetOSVersion(Process *process) {
   if (process && strstr(GetPluginName().GetCString(), "-simulator")) {
     lldb_private::ProcessInstanceInfo proc_info;
     if (Host::GetProcessInfo(process->GetID(), proc_info)) {
       const Environment &env = proc_info.GetEnvironment();
 
-      if (Args::StringToVersion(env.lookup("SIMULATOR_RUNTIME_VERSION"), major,
-                                minor, update))
-        return true;
+      llvm::VersionTuple result;
+      if (!result.tryParse(env.lookup("SIMULATOR_RUNTIME_VERSION")))
+        return result;
 
       std::string dyld_root_path = env.lookup("DYLD_ROOT_PATH");
       if (!dyld_root_path.empty()) {
@@ -1670,17 +1629,18 @@
         std::string product_version;
         if (system_version_plist.GetValueAsString("ProductVersion",
                                                   product_version)) {
-          return Args::StringToVersion(product_version, major, minor, update);
+          if (!result.tryParse(product_version))
+            return result;
         }
       }
     }
     // For simulator platforms, do NOT call back through
     // Platform::GetOSVersion() as it might call Process::GetHostOSVersion()
     // which we don't want as it will be incorrect
-    return false;
+    return llvm::VersionTuple();
   }
 
-  return Platform::GetOSVersion(major, minor, update, process);
+  return Platform::GetOSVersion(process);
 }
 
 lldb_private::FileSpec PlatformDarwin::LocateExecutable(const char *basename) {
diff --git a/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/source/Plugins/Platform/MacOSX/PlatformDarwin.h
index c04318e..3ad29ec 100644
--- a/source/Plugins/Platform/MacOSX/PlatformDarwin.h
+++ b/source/Plugins/Platform/MacOSX/PlatformDarwin.h
@@ -69,8 +69,8 @@
 
   void CalculateTrapHandlerSymbolNames() override;
 
-  bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update,
-                    lldb_private::Process *process = nullptr) override;
+  llvm::VersionTuple
+  GetOSVersion(lldb_private::Process *process = nullptr) override;
 
   bool SupportsModules() override { return true; }
 
@@ -82,7 +82,7 @@
   lldb_private::Status
   LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) override;
 
-  static std::tuple<uint32_t, uint32_t, uint32_t, llvm::StringRef>
+  static std::tuple<llvm::VersionTuple, llvm::StringRef>
   ParseVersionBuildDir(llvm::StringRef str);
 
 protected:
@@ -101,8 +101,7 @@
     iPhoneOS,
   };
 
-  static bool SDKSupportsModules(SDKType sdk_type, uint32_t major,
-                                 uint32_t minor, uint32_t micro);
+  static bool SDKSupportsModules(SDKType sdk_type, llvm::VersionTuple version);
 
   static bool SDKSupportsModules(SDKType desired_type,
                                  const lldb_private::FileSpec &sdk_path);
diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp b/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp
index 7b78c67..930d062 100644
--- a/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp
+++ b/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp
@@ -31,12 +31,10 @@
 
 PlatformRemoteDarwinDevice::SDKDirectoryInfo::SDKDirectoryInfo(
     const lldb_private::FileSpec &sdk_dir)
-    : directory(sdk_dir), build(), version_major(0), version_minor(0),
-      version_update(0), user_cached(false) {
+    : directory(sdk_dir), build(), user_cached(false) {
   llvm::StringRef dirname_str = sdk_dir.GetFilename().GetStringRef();
   llvm::StringRef build_str;
-  std::tie(version_major, version_minor, version_update, build_str) =
-      ParseVersionBuildDir(dirname_str);
+  std::tie(version, build_str) = ParseVersionBuildDir(dirname_str);
   build.SetString(build_str);
 }
 
@@ -255,24 +253,23 @@
 
     // If we are connected we can find the version of the OS the platform us
     // running on and select the right SDK
-    uint32_t major, minor, update;
-    if (GetOSVersion(major, minor, update)) {
+    llvm::VersionTuple version = GetOSVersion();
+    if (!version.empty()) {
       if (UpdateSDKDirectoryInfosIfNeeded()) {
         // First try for an exact match of major, minor and update
         for (i = 0; i < num_sdk_infos; ++i) {
           if (check_sdk_info[i]) {
-            if (m_sdk_directory_infos[i].version_major == major &&
-                m_sdk_directory_infos[i].version_minor == minor &&
-                m_sdk_directory_infos[i].version_update == update) {
+            if (m_sdk_directory_infos[i].version == version)
               return &m_sdk_directory_infos[i];
-            }
           }
         }
         // First try for an exact match of major and minor
         for (i = 0; i < num_sdk_infos; ++i) {
           if (check_sdk_info[i]) {
-            if (m_sdk_directory_infos[i].version_major == major &&
-                m_sdk_directory_infos[i].version_minor == minor) {
+            if (m_sdk_directory_infos[i].version.getMajor() ==
+                    version.getMajor() &&
+                m_sdk_directory_infos[i].version.getMinor() ==
+                    version.getMinor()) {
               return &m_sdk_directory_infos[i];
             }
           }
@@ -280,7 +277,8 @@
         // Lastly try to match of major version only..
         for (i = 0; i < num_sdk_infos; ++i) {
           if (check_sdk_info[i]) {
-            if (m_sdk_directory_infos[i].version_major == major) {
+            if (m_sdk_directory_infos[i].version.getMajor() ==
+                version.getMajor()) {
               return &m_sdk_directory_infos[i];
             }
           }
@@ -300,25 +298,13 @@
 PlatformRemoteDarwinDevice::GetSDKDirectoryForLatestOSVersion() {
   const PlatformRemoteDarwinDevice::SDKDirectoryInfo *result = NULL;
   if (UpdateSDKDirectoryInfosIfNeeded()) {
-    const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
-    // First try for an exact match of major, minor and update
-    for (uint32_t i = 0; i < num_sdk_infos; ++i) {
-      const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i];
-      if (sdk_dir_info.version_major != UINT32_MAX) {
-        if (result == NULL ||
-            sdk_dir_info.version_major > result->version_major) {
-          result = &sdk_dir_info;
-        } else if (sdk_dir_info.version_major == result->version_major) {
-          if (sdk_dir_info.version_minor > result->version_minor) {
-            result = &sdk_dir_info;
-          } else if (sdk_dir_info.version_minor == result->version_minor) {
-            if (sdk_dir_info.version_update > result->version_update) {
-              result = &sdk_dir_info;
-            }
-          }
-        }
-      }
-    }
+    auto max = std::max_element(
+        m_sdk_directory_infos.begin(), m_sdk_directory_infos.end(),
+        [](const SDKDirectoryInfo &a, const SDKDirectoryInfo &b) {
+          return a.version < b.version;
+        });
+    if (max != m_sdk_directory_infos.end())
+      result = &*max;
   }
   return result;
 }
diff --git a/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h b/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h
index f159e85..8ddfd51 100644
--- a/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h
+++ b/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h
@@ -60,9 +60,7 @@
     SDKDirectoryInfo(const lldb_private::FileSpec &sdk_dir_spec);
     lldb_private::FileSpec directory;
     lldb_private::ConstString build;
-    uint32_t version_major;
-    uint32_t version_minor;
-    uint32_t version_update;
+    llvm::VersionTuple version;
     bool user_cached;
   };
 
diff --git a/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp b/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
index d3f4339..9ac1638 100644
--- a/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
+++ b/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
@@ -653,9 +653,10 @@
 }
 
 bool PlatformPOSIX::GetRemoteOSVersion() {
-  if (m_remote_platform_sp)
-    return m_remote_platform_sp->GetOSVersion(
-        m_major_os_version, m_minor_os_version, m_update_os_version);
+  if (m_remote_platform_sp) {
+    m_os_version = m_remote_platform_sp->GetOSVersion();
+    return !m_os_version.empty();
+  }
   return false;
 }
 
diff --git a/source/Plugins/Platform/Windows/PlatformWindows.cpp b/source/Plugins/Platform/Windows/PlatformWindows.cpp
index 5501710..c752d64 100644
--- a/source/Plugins/Platform/Windows/PlatformWindows.cpp
+++ b/source/Plugins/Platform/Windows/PlatformWindows.cpp
@@ -280,9 +280,10 @@
 }
 
 bool PlatformWindows::GetRemoteOSVersion() {
-  if (m_remote_platform_sp)
-    return m_remote_platform_sp->GetOSVersion(
-        m_major_os_version, m_minor_os_version, m_update_os_version);
+  if (m_remote_platform_sp) {
+    m_os_version = m_remote_platform_sp->GetOSVersion();
+    return !m_os_version.empty();
+  }
   return false;
 }
 
diff --git a/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
index 10cff9e..348bb82 100644
--- a/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
+++ b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -236,14 +236,8 @@
 }
 
 bool PlatformRemoteGDBServer::GetRemoteOSVersion() {
-  uint32_t major, minor, update;
-  if (m_gdb_client.GetOSVersion(major, minor, update)) {
-    m_major_os_version = major;
-    m_minor_os_version = minor;
-    m_update_os_version = update;
-    return true;
-  }
-  return false;
+  m_os_version = m_gdb_client.GetOSVersion();
+  return !m_os_version.empty();
 }
 
 bool PlatformRemoteGDBServer::GetRemoteOSBuildString(std::string &s) {
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 6948339..319fa65 100644
--- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -100,11 +100,10 @@
       m_curr_pid(LLDB_INVALID_PROCESS_ID), m_curr_tid(LLDB_INVALID_THREAD_ID),
       m_curr_tid_run(LLDB_INVALID_THREAD_ID),
       m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(),
-      m_os_version_major(UINT32_MAX), m_os_version_minor(UINT32_MAX),
-      m_os_version_update(UINT32_MAX), m_os_build(), m_os_kernel(),
-      m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX),
-      m_default_packet_timeout(0), m_max_packet_size(0),
-      m_qSupported_response(), m_supported_async_json_packets_is_valid(false),
+      m_os_build(), m_os_kernel(), m_hostname(), m_gdb_server_name(),
+      m_gdb_server_version(UINT32_MAX), m_default_packet_timeout(0),
+      m_max_packet_size(0), m_qSupported_response(),
+      m_supported_async_json_packets_is_valid(false),
       m_supported_async_json_packets_sp(), m_qXfer_memory_map(),
       m_qXfer_memory_map_loaded(false) {}
 
@@ -323,9 +322,7 @@
     m_qSymbol_requests_done = false;
     m_supports_qModuleInfo = true;
     m_host_arch.Clear();
-    m_os_version_major = UINT32_MAX;
-    m_os_version_minor = UINT32_MAX;
-    m_os_version_update = UINT32_MAX;
+    m_os_version = llvm::VersionTuple();
     m_os_build.clear();
     m_os_kernel.clear();
     m_hostname.clear();
@@ -944,18 +941,9 @@
   return -1;
 }
 
-bool GDBRemoteCommunicationClient::GetOSVersion(uint32_t &major,
-                                                uint32_t &minor,
-                                                uint32_t &update) {
-  if (GetHostInfo()) {
-    if (m_os_version_major != UINT32_MAX) {
-      major = m_os_version_major;
-      minor = m_os_version_minor;
-      update = m_os_version_update;
-      return true;
-    }
-  }
-  return false;
+llvm::VersionTuple GDBRemoteCommunicationClient::GetOSVersion() {
+  GetHostInfo();
+  return m_os_version;
 }
 
 bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) {
@@ -1218,9 +1206,7 @@
                                      // "version" key instead of
                                      // "os_version"...
           {
-            Args::StringToVersion(value, m_os_version_major, m_os_version_minor,
-                                  m_os_version_update);
-            if (m_os_version_major != UINT32_MAX)
+            if (!m_os_version.tryParse(value))
               ++num_keys_decoded;
           } else if (name.equals("watchpoint_exceptions_received")) {
             m_watchpoints_trigger_after_instruction =
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index fcf578f..cf1d249 100644
--- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -266,7 +266,7 @@
 
   bool GetDefaultThreadId(lldb::tid_t &tid);
 
-  bool GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update);
+  llvm::VersionTuple GetOSVersion();
 
   bool GetOSBuildString(std::string &s);
 
@@ -573,9 +573,7 @@
 
   ArchSpec m_host_arch;
   ArchSpec m_process_arch;
-  uint32_t m_os_version_major;
-  uint32_t m_os_version_minor;
-  uint32_t m_os_version_update;
+  llvm::VersionTuple m_os_version;
   std::string m_os_build;
   std::string m_os_kernel;
   std::string m_hostname;
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
index d0cdbf3..c5b4783 100644
--- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -269,19 +269,10 @@
     break;
   }
 
-  uint32_t major = UINT32_MAX;
-  uint32_t minor = UINT32_MAX;
-  uint32_t update = UINT32_MAX;
-  if (HostInfo::GetOSVersion(major, minor, update)) {
-    if (major != UINT32_MAX) {
-      response.Printf("os_version:%u", major);
-      if (minor != UINT32_MAX) {
-        response.Printf(".%u", minor);
-        if (update != UINT32_MAX)
-          response.Printf(".%u", update);
-      }
-      response.PutChar(';');
-    }
+  llvm::VersionTuple version = HostInfo::GetOSVersion();
+  if (!version.empty()) {
+    response.Format("os_version:{0}", version.getAsString());
+    response.PutChar(';');
   }
 
   std::string s;
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index e65f2f3..648df1a 100644
--- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -4241,13 +4241,8 @@
   }
 }
 
-bool ProcessGDBRemote::GetHostOSVersion(uint32_t &major, uint32_t &minor,
-                                        uint32_t &update) {
-  if (m_gdb_comm.GetOSVersion(major, minor, update))
-    return true;
-  // We failed to get the host OS version, defer to the base implementation to
-  // correctly invalidate the arguments.
-  return Process::GetHostOSVersion(major, minor, update);
+llvm::VersionTuple ProcessGDBRemote::GetHostOSVersion() {
+  return m_gdb_comm.GetOSVersion();
 }
 
 namespace {
diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index f0b68b2..45bb2d4 100644
--- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -217,8 +217,7 @@
   void PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs,
                            const llvm::Triple &triple) override;
 
-  bool GetHostOSVersion(uint32_t &major, uint32_t &minor,
-                        uint32_t &update) override;
+  llvm::VersionTuple GetHostOSVersion() override;
 
   size_t LoadModules(LoadedModuleInfoList &module_list) override;
 
diff --git a/source/Target/Platform.cpp b/source/Target/Platform.cpp
index be94f37..f18ccff 100644
--- a/source/Target/Platform.cpp
+++ b/source/Target/Platform.cpp
@@ -369,13 +369,11 @@
 Platform::Platform(bool is_host)
     : m_is_host(is_host), m_os_version_set_while_connected(false),
       m_system_arch_set_while_connected(false), m_sdk_sysroot(), m_sdk_build(),
-      m_working_dir(), m_remote_url(), m_name(), m_major_os_version(UINT32_MAX),
-      m_minor_os_version(UINT32_MAX), m_update_os_version(UINT32_MAX),
-      m_system_arch(), m_mutex(), m_uid_map(), m_gid_map(),
-      m_max_uid_name_len(0), m_max_gid_name_len(0), m_supports_rsync(false),
-      m_rsync_opts(), m_rsync_prefix(), m_supports_ssh(false), m_ssh_opts(),
-      m_ignores_remote_hostname(false), m_trap_handlers(),
-      m_calculated_trap_handlers(false),
+      m_working_dir(), m_remote_url(), m_name(), m_system_arch(), m_mutex(),
+      m_uid_map(), m_gid_map(), m_max_uid_name_len(0), m_max_gid_name_len(0),
+      m_supports_rsync(false), m_rsync_opts(), m_rsync_prefix(),
+      m_supports_ssh(false), m_ssh_opts(), m_ignores_remote_hostname(false),
+      m_trap_handlers(), m_calculated_trap_handlers(false),
       m_module_cache(llvm::make_unique<ModuleCache>()) {
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
   if (log)
@@ -395,9 +393,6 @@
 }
 
 void Platform::GetStatus(Stream &strm) {
-  uint32_t major = UINT32_MAX;
-  uint32_t minor = UINT32_MAX;
-  uint32_t update = UINT32_MAX;
   std::string s;
   strm.Printf("  Platform: %s\n", GetPluginName().GetCString());
 
@@ -410,12 +405,9 @@
     }
   }
 
-  if (GetOSVersion(major, minor, update)) {
-    strm.Printf("OS Version: %u", major);
-    if (minor != UINT32_MAX)
-      strm.Printf(".%u", minor);
-    if (update != UINT32_MAX)
-      strm.Printf(".%u", update);
+  llvm::VersionTuple os_version = GetOSVersion();
+  if (!os_version.empty()) {
+    strm.Format("OS Version: {0}", os_version.getAsString());
 
     if (GetOSBuildString(s))
       strm.Printf(" (%s)", s.c_str());
@@ -447,17 +439,14 @@
     strm.Printf("Platform-specific connection: %s\n", specific_info.c_str());
 }
 
-bool Platform::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update,
-                            Process *process) {
+llvm::VersionTuple Platform::GetOSVersion(Process *process) {
   std::lock_guard<std::mutex> guard(m_mutex);
 
-  bool success = m_major_os_version != UINT32_MAX;
   if (IsHost()) {
-    if (!success) {
+    if (m_os_version.empty()) {
       // We have a local host platform
-      success = HostInfo::GetOSVersion(m_major_os_version, m_minor_os_version,
-                                       m_update_os_version);
-      m_os_version_set_while_connected = success;
+      m_os_version = HostInfo::GetOSVersion();
+      m_os_version_set_while_connected = !m_os_version.empty();
     }
   } else {
     // We have a remote platform. We can only fetch the remote
@@ -467,7 +456,7 @@
     const bool is_connected = IsConnected();
 
     bool fetch = false;
-    if (success) {
+    if (!m_os_version.empty()) {
       // We have valid OS version info, check to make sure it wasn't manually
       // set prior to connecting. If it was manually set prior to connecting,
       // then lets fetch the actual OS version info if we are now connected.
@@ -478,22 +467,18 @@
       fetch = is_connected;
     }
 
-    if (fetch) {
-      success = GetRemoteOSVersion();
-      m_os_version_set_while_connected = success;
-    }
+    if (fetch)
+      m_os_version_set_while_connected = GetRemoteOSVersion();
   }
 
-  if (success) {
-    major = m_major_os_version;
-    minor = m_minor_os_version;
-    update = m_update_os_version;
-  } else if (process) {
+  if (!m_os_version.empty())
+    return m_os_version;
+  if (process) {
     // Check with the process in case it can answer the question if a process
     // was provided
-    return process->GetHostOSVersion(major, minor, update);
+    return process->GetHostOSVersion();
   }
-  return success;
+  return llvm::VersionTuple();
 }
 
 bool Platform::GetOSBuildString(std::string &s) {
@@ -859,7 +844,7 @@
   return nullptr;
 }
 
-bool Platform::SetOSVersion(uint32_t major, uint32_t minor, uint32_t update) {
+bool Platform::SetOSVersion(llvm::VersionTuple version) {
   if (IsHost()) {
     // We don't need anyone setting the OS version for the host platform, we
     // should be able to figure it out by calling HostInfo::GetOSVersion(...).
@@ -874,9 +859,7 @@
       // We aren't connected and we might want to set the OS version ahead of
       // time before we connect so we can peruse files and use a local SDK or
       // PDK cache of support files to disassemble or do other things.
-      m_major_os_version = major;
-      m_minor_os_version = minor;
-      m_update_os_version = update;
+      m_os_version = version;
       return true;
     }
   }
diff --git a/source/Utility/Args.cpp b/source/Utility/Args.cpp
index 16d6b71..4f65cda 100644
--- a/source/Utility/Args.cpp
+++ b/source/Utility/Args.cpp
@@ -409,29 +409,6 @@
   return s.c_str();
 }
 
-bool Args::StringToVersion(llvm::StringRef string, uint32_t &major,
-                           uint32_t &minor, uint32_t &update) {
-  major = UINT32_MAX;
-  minor = UINT32_MAX;
-  update = UINT32_MAX;
-
-  if (string.empty())
-    return false;
-
-  llvm::StringRef major_str, minor_str, update_str;
-
-  std::tie(major_str, minor_str) = string.split('.');
-  std::tie(minor_str, update_str) = minor_str.split('.');
-  if (major_str.getAsInteger(10, major))
-    return false;
-  if (!minor_str.empty() && minor_str.getAsInteger(10, minor))
-    return false;
-  if (!update_str.empty() && update_str.getAsInteger(10, update))
-    return false;
-
-  return true;
-}
-
 const char *Args::GetShellSafeArgument(const FileSpec &shell,
                                        const char *unsafe_arg,
                                        std::string &safe_arg) {
diff --git a/unittests/Platform/PlatformDarwinTest.cpp b/unittests/Platform/PlatformDarwinTest.cpp
index a16e2d9..18cd1b7 100644
--- a/unittests/Platform/PlatformDarwinTest.cpp
+++ b/unittests/Platform/PlatformDarwinTest.cpp
@@ -19,38 +19,29 @@
 using namespace lldb_private;
 
 TEST(PlatformDarwinTest, TestParseVersionBuildDir) {
-  uint32_t A, B, C;
+  llvm::VersionTuple V;
   llvm::StringRef D;
 
-  std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("1.2.3 (test1)");
-  EXPECT_EQ(1u, A);
-  EXPECT_EQ(2u, B);
-  EXPECT_EQ(3u, C);
+  std::tie(V, D) = PlatformDarwin::ParseVersionBuildDir("1.2.3 (test1)");
+  EXPECT_EQ(llvm::VersionTuple(1, 2, 3), V);
   EXPECT_EQ("test1", D);
 
-  std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("2.3 (test2)");
-  EXPECT_EQ(2u, A);
-  EXPECT_EQ(3u, B);
+  std::tie(V, D) = PlatformDarwin::ParseVersionBuildDir("2.3 (test2)");
+  EXPECT_EQ(llvm::VersionTuple(2, 3), V);
   EXPECT_EQ("test2", D);
 
-  std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("3 (test3)");
-  EXPECT_EQ(3u, A);
+  std::tie(V, D) = PlatformDarwin::ParseVersionBuildDir("3 (test3)");
+  EXPECT_EQ(llvm::VersionTuple(3), V);
   EXPECT_EQ("test3", D);
 
-  std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("1.2.3 (test");
-  EXPECT_EQ(1u, A);
-  EXPECT_EQ(2u, B);
-  EXPECT_EQ(3u, C);
+  std::tie(V, D) = PlatformDarwin::ParseVersionBuildDir("1.2.3 (test");
+  EXPECT_EQ(llvm::VersionTuple(1, 2, 3), V);
   EXPECT_EQ("test", D);
 
-  std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("2.3.4 test");
-  EXPECT_EQ(2u, A);
-  EXPECT_EQ(3u, B);
-  EXPECT_EQ(4u, C);
+  std::tie(V, D) = PlatformDarwin::ParseVersionBuildDir("2.3.4 test");
+  EXPECT_EQ(llvm::VersionTuple(2, 3, 4), V);
   EXPECT_EQ("", D);
 
-  std::tie(A, B, C, D) = PlatformDarwin::ParseVersionBuildDir("3.4.5");
-  EXPECT_EQ(3u, A);
-  EXPECT_EQ(4u, B);
-  EXPECT_EQ(5u, C);
+  std::tie(V, D) = PlatformDarwin::ParseVersionBuildDir("3.4.5");
+  EXPECT_EQ(llvm::VersionTuple(3, 4, 5), V);
 }