Index: llvm/include/llvm/Support/FileSystem.h
===================================================================
--- llvm/include/llvm/Support/FileSystem.h
+++ llvm/include/llvm/Support/FileSystem.h
@@ -339,6 +339,16 @@
 /// specific error_code.
 std::error_code create_hard_link(const Twine &to, const Twine &from);
 
+/// @brief Collapse all . and .. patterns, resolve all symlinks, and replace
+/// special folders (e.g. ~ with their real path).
+///
+/// @param path The path to resolve.
+/// @param output The location to store the resolved path.
+/// @param shell_expand If true, expands shell-specific constructs such as ~
+///                     (on posix platforms).
+std::error_code real_path(const Twine &path, SmallVectorImpl<char> &output,
+                          bool shell_expand = false);
+
 /// @brief Get the current path.
 ///
 /// @param result Holds the current path on return.
Index: llvm/lib/Support/Unix/Path.inc
===================================================================
--- llvm/lib/Support/Unix/Path.inc
+++ llvm/lib/Support/Unix/Path.inc
@@ -48,6 +48,8 @@
 # endif
 #endif
 
+#include <pwd.h>
+
 #ifdef __APPLE__
 #include <mach-o/dyld.h>
 #include <sys/attr.h>
@@ -475,6 +477,45 @@
   return std::error_code();
 }
 
+static void expand_tilde_expr(const Twine &Path, SmallVectorImpl<char> &Dest) {
+  SmallString<64> ResultStorage;
+  StringRef P = Path.toNullTerminatedStringRef(ResultStorage);
+  Dest.clear();
+  if (P.empty() || !P.startswith("~")) {
+    Dest.append(P.begin(), P.end());
+    return;
+  }
+
+  // LLVM's normal path functions don't treat ~ as a directory name, so
+  // root_name() / relative_name() don't work, we have to do the split manually.
+  StringRef Root, Relative;
+  std::tie(Root, Relative) = P.split('/');
+  if (Root.size() == 1) {
+    // A path of ~/ resolves to the current user's home dir.
+    llvm::sys::path::home_directory(Dest);
+  } else {
+    // This is a string of the form ~username/, look up this user's entry in the
+    // password database.
+    struct passwd *Entry = nullptr;
+    if (P.size() == Root.size()) {
+      // We're already null terminated.
+      Entry = ::getpwnam(Root.drop_front().data());
+    } else {
+      std::string User = Root.drop_front().str();
+      Entry = ::getpwnam(User.c_str());
+    }
+
+    if (!Entry) {
+      // Unable to look up the entry, just return back the original path.
+      Dest.append(P.begin(), P.end());
+      return;
+    }
+    Dest.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir));
+  }
+
+  llvm::sys::path::append(Dest, Relative);
+}
+
 static std::error_code fillStatus(int StatRet, const struct stat &Status,
                              file_status &Result) {
   if (StatRet != 0) {
@@ -795,6 +836,28 @@
   return std::error_code();
 }
 
+std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest, bool shell_expand) {
+  int fd;
+  dest.clear();
+  SmallString<64> Storage;
+  auto S = path.toNullTerminatedStringRef(Storage);
+  if (S.empty())
+    return std::error_code();
+  std::error_code EC;
+  if (shell_expand && (S[0] == '~')) {
+    // open() doesn't accept tilde expressions, they are expanded by the shell.
+    // So expand then first before trying to resolve the file.
+    expand_tilde_expr(path, dest);
+    EC = openFileForRead(dest, fd, &dest);
+  } else
+    EC = openFileForRead(path, fd, &dest);
+
+  if (EC)
+    return EC;
+  ::close(fd);
+  return std::error_code();
+}
+
 } // end namespace fs
 
 namespace path {
@@ -805,7 +868,6 @@
     result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
     return true;
   }
-
   return false;
 }
 
Index: llvm/lib/Support/Windows/Path.inc
===================================================================
--- llvm/lib/Support/Windows/Path.inc
+++ llvm/lib/Support/Windows/Path.inc
@@ -778,6 +778,40 @@
   return std::error_code();
 }
 
+static void realPathFromHandle(HANDLE H, SmallVectorImpl<char> &RealPath) {
+  RealPath.clear();
+  wchar_t RealPathUTF16[MAX_PATH];
+  DWORD CountChars = ::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH,
+                                                 FILE_NAME_NORMALIZED);
+  if (CountChars > 0 && CountChars < MAX_PATH) {
+    // Convert the result from UTF-16 to UTF-8.
+    SmallString<MAX_PATH> RealPathUTF8;
+    if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8)) {
+      StringRef Ref(RealPathUTF8);
+      Ref.consume_front(R"(\\?\)");
+      RealPath.append(Ref.begin(), Ref.end());
+    }
+  }
+}
+
+static std::error_code directoryRealPath(const Twine &Name,
+                                         SmallVectorImpl<char> &RealPath) {
+  SmallVector<wchar_t, 128> PathUTF16;
+
+  if (std::error_code EC = widenPath(Name, PathUTF16))
+    return EC;
+
+  HANDLE H =
+      ::CreateFileW(PathUTF16.begin(), GENERIC_READ,
+                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                    NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+  if (H == INVALID_HANDLE_VALUE)
+    return mapWindowsError(GetLastError());
+  realPathFromHandle(H, RealPath);
+  ::CloseHandle(H);
+  return std::error_code();
+}
+
 std::error_code openFileForRead(const Twine &Name, int &ResultFD,
                                 SmallVectorImpl<char> *RealPath) {
   SmallVector<wchar_t, 128> PathUTF16;
@@ -809,20 +843,8 @@
   }
 
   // Fetch the real name of the file, if the user asked
-  if (RealPath) {
-    RealPath->clear();
-    wchar_t RealPathUTF16[MAX_PATH];
-    DWORD CountChars =
-      ::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH,
-                                  FILE_NAME_NORMALIZED);
-    if (CountChars > 0 && CountChars < MAX_PATH) {
-      // Convert the result from UTF-16 to UTF-8.
-      SmallString<MAX_PATH> RealPathUTF8;
-      if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8))
-        RealPath->append(RealPathUTF8.data(),
-                         RealPathUTF8.data() + strlen(RealPathUTF8.data()));
-    }
-  }
+  if (RealPath)
+    realPathFromHandle(H, *RealPath);
 
   ResultFD = FD;
   return std::error_code();
@@ -920,6 +942,20 @@
 
   return windows::UTF16ToUTF8(TempPath.data(), CharCount, ResultPath);
 }
+
+std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest, bool shell_expand) {
+  (void)shell_expand;
+
+  if (is_directory(path))
+    return directoryRealPath(path, dest);
+
+  int fd;
+  if (std::error_code EC = llvm::sys::fs::openFileForRead(path, fd, &dest))
+    return EC;
+  ::close(fd);
+  return std::error_code();
+}
+
 } // end namespace fs
 
 namespace path {
Index: llvm/unittests/Support/Path.cpp
===================================================================
--- llvm/unittests/Support/Path.cpp
+++ llvm/unittests/Support/Path.cpp
@@ -524,6 +524,29 @@
   ASSERT_NO_ERROR(fs::remove(TempPath));
 }
 
+TEST_F(FileSystemTest, RealPath) {
+  ASSERT_NO_ERROR(
+      fs::create_directories(Twine(TestDirectory) + "/test1/test2/test3"));
+  ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/test1/test2/test3"));
+
+  SmallString<64> RealBase;
+  SmallString<64> Expected;
+  SmallString<64> Actual;
+
+  // TestDirectory itself might be under a symlink or have been specified with
+  // a different case than the existing temp directory.  In such cases real_path
+  // on the concatenated path will differ in the TestDirectory portion from
+  // how we specified it.  Make sure to compare against the real_path of the
+  // TestDirectory, and not just the value of TestDirectory.
+  ASSERT_NO_ERROR(fs::real_path(TestDirectory, RealBase));
+  path::native(Twine(RealBase) + "/test1/test2", Expected);
+
+  ASSERT_NO_ERROR(fs::real_path(
+      Twine(TestDirectory) + "/././test1/../test1/test2/./test3/..", Actual));
+
+  EXPECT_EQ(Expected, Actual);
+}
+
 TEST_F(FileSystemTest, TempFiles) {
   // Create a temp file.
   int FileDescriptor;