diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index f1f3a0519bbcb..e56c9928dccd6 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -24,6 +24,7 @@
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(set_stdio)]
+#![feature(rustc_stack_internals)]
 
 extern crate arena;
 extern crate getopts;
@@ -1461,16 +1462,56 @@ pub fn in_rustc_thread<F, R>(f: F) -> Result<R, Box<Any + Send>>
     // Temporarily have stack size set to 16MB to deal with nom-using crates failing
     const STACK_SIZE: usize = 16 * 1024 * 1024; // 16MB
 
-    let mut cfg = thread::Builder::new().name("rustc".to_string());
+    #[cfg(unix)]
+    let spawn_thread = unsafe {
+        // Fetch the current resource limits
+        let mut rlim = libc::rlimit {
+            rlim_cur: 0,
+            rlim_max: 0,
+        };
+        if libc::getrlimit(libc::RLIMIT_STACK, &mut rlim) != 0 {
+            let err = io::Error::last_os_error();
+            error!("in_rustc_thread: error calling getrlimit: {}", err);
+            true
+        } else if rlim.rlim_max < STACK_SIZE as libc::rlim_t {
+            true
+        } else {
+            std::rt::deinit_stack_guard();
+            rlim.rlim_cur = STACK_SIZE as libc::rlim_t;
+            if libc::setrlimit(libc::RLIMIT_STACK, &mut rlim) != 0 {
+                let err = io::Error::last_os_error();
+                error!("in_rustc_thread: error calling setrlimit: {}", err);
+                std::rt::update_stack_guard();
+                true
+            } else {
+                std::rt::update_stack_guard();
+                false
+            }
+        }
+    };
 
-    // FIXME: Hacks on hacks. If the env is trying to override the stack size
-    // then *don't* set it explicitly.
-    if env::var_os("RUST_MIN_STACK").is_none() {
-        cfg = cfg.stack_size(STACK_SIZE);
-    }
+    // We set the stack size at link time. See src/rustc/rustc.rs.
+    #[cfg(windows)]
+    let spawn_thread = false;
+
+    #[cfg(not(any(windows,unix)))]
+    let spawn_thread = true;
 
-    let thread = cfg.spawn(f);
-    thread.unwrap().join()
+    // The or condition is added from backward compatibility.
+    if spawn_thread || env::var_os("RUST_MIN_STACK").is_some() {
+        let mut cfg = thread::Builder::new().name("rustc".to_string());
+
+        // FIXME: Hacks on hacks. If the env is trying to override the stack size
+        // then *don't* set it explicitly.
+        if env::var_os("RUST_MIN_STACK").is_none() {
+            cfg = cfg.stack_size(STACK_SIZE);
+        }
+
+        let thread = cfg.spawn(f);
+        thread.unwrap().join()
+    } else {
+        Ok(f())
+    }
 }
 
 /// Get a list of extra command-line flags provided by the user, as strings.
diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs
index e1392762a59dc..8f945470b7e94 100644
--- a/src/libstd/rt.rs
+++ b/src/libstd/rt.rs
@@ -73,3 +73,18 @@ fn lang_start<T: ::process::Termination + 'static>
 {
     lang_start_internal(&move || main().report(), argc, argv)
 }
+
+/// Function used for reverting changes to the main stack before setrlimit().
+/// This is POSIX (non-Linux) specific and unlikely to be directly stabilized.
+#[unstable(feature = "rustc_stack_internals", issue = "0")]
+pub unsafe fn deinit_stack_guard() {
+    ::sys::thread::guard::deinit();
+}
+
+/// Function used for resetting the main stack guard address after setrlimit().
+/// This is POSIX specific and unlikely to be directly stabilized.
+#[unstable(feature = "rustc_stack_internals", issue = "0")]
+pub unsafe fn update_stack_guard() {
+    let main_guard = ::sys::thread::guard::init();
+    ::sys_common::thread_info::reset_guard(main_guard);
+}
diff --git a/src/libstd/sys/cloudabi/thread.rs b/src/libstd/sys/cloudabi/thread.rs
index 78a3b82546e3a..a22d9053b6964 100644
--- a/src/libstd/sys/cloudabi/thread.rs
+++ b/src/libstd/sys/cloudabi/thread.rs
@@ -118,6 +118,7 @@ pub mod guard {
     pub unsafe fn init() -> Option<Guard> {
         None
     }
+    pub unsafe fn deinit() {}
 }
 
 fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs
index c4719a94c7e9d..f20350269b7cf 100644
--- a/src/libstd/sys/redox/thread.rs
+++ b/src/libstd/sys/redox/thread.rs
@@ -91,4 +91,5 @@ pub mod guard {
     pub type Guard = !;
     pub unsafe fn current() -> Option<Guard> { None }
     pub unsafe fn init() -> Option<Guard> { None }
+    pub unsafe fn deinit() {}
 }
diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs
index 72cdb9440b8e7..2db3d4a5744e7 100644
--- a/src/libstd/sys/unix/thread.rs
+++ b/src/libstd/sys/unix/thread.rs
@@ -209,6 +209,7 @@ pub mod guard {
     pub type Guard = Range<usize>;
     pub unsafe fn current() -> Option<Guard> { None }
     pub unsafe fn init() -> Option<Guard> { None }
+    pub unsafe fn deinit() {}
 }
 
 
@@ -222,8 +223,8 @@ pub mod guard {
 #[cfg_attr(test, allow(dead_code))]
 pub mod guard {
     use libc;
-    use libc::mmap;
-    use libc::{PROT_NONE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED};
+    use libc::{mmap, mprotect};
+    use libc::{PROT_NONE, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED};
     use ops::Range;
     use sys::os;
 
@@ -284,10 +285,10 @@ pub mod guard {
         ret
     }
 
-    pub unsafe fn init() -> Option<Guard> {
-        PAGE_SIZE = os::page_size();
-
-        let mut stackaddr = get_stack_start()?;
+    // Precondition: PAGE_SIZE is initialized.
+    unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> {
+        assert!(PAGE_SIZE != 0);
+        let stackaddr = get_stack_start()?;
 
         // Ensure stackaddr is page aligned! A parent process might
         // have reset RLIMIT_STACK to be non-page aligned. The
@@ -296,10 +297,17 @@ pub mod guard {
         // page-aligned, calculate the fix such that stackaddr <
         // new_page_aligned_stackaddr < stackaddr + stacksize
         let remainder = (stackaddr as usize) % PAGE_SIZE;
-        if remainder != 0 {
-            stackaddr = ((stackaddr as usize) + PAGE_SIZE - remainder)
-                as *mut libc::c_void;
-        }
+        Some(if remainder == 0 {
+            stackaddr
+        } else {
+            ((stackaddr as usize) + PAGE_SIZE - remainder) as *mut libc::c_void
+        })
+    }
+
+    pub unsafe fn init() -> Option<Guard> {
+        PAGE_SIZE = os::page_size();
+
+        let stackaddr = get_stack_start_aligned()?;
 
         if cfg!(target_os = "linux") {
             // Linux doesn't allocate the whole stack right away, and
@@ -336,6 +344,26 @@ pub mod guard {
         }
     }
 
+    pub unsafe fn deinit() {
+        if !cfg!(target_os = "linux") {
+            if let Some(stackaddr) = get_stack_start_aligned() {
+                // Remove the protection on the guard page.
+                // FIXME: we cannot unmap the page, because when we mmap()
+                // above it may be already mapped by the OS, which we can't
+                // detect from mmap()'s return value. If we unmap this page,
+                // it will lead to failure growing stack size on platforms like
+                // macOS. Instead, just restore the page to a writable state.
+                // This ain't Linux, so we probably don't need to care about
+                // execstack.
+                let result = mprotect(stackaddr, PAGE_SIZE, PROT_READ | PROT_WRITE);
+
+                if result != 0 {
+                    panic!("unable to reset the guard page");
+                }
+            }
+        }
+    }
+
     #[cfg(any(target_os = "macos",
               target_os = "bitrig",
               target_os = "openbsd",
diff --git a/src/libstd/sys/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs
index 6a066509b492a..7345843b975e4 100644
--- a/src/libstd/sys/wasm/thread.rs
+++ b/src/libstd/sys/wasm/thread.rs
@@ -46,4 +46,5 @@ pub mod guard {
     pub type Guard = !;
     pub unsafe fn current() -> Option<Guard> { None }
     pub unsafe fn init() -> Option<Guard> { None }
+    pub unsafe fn deinit() {}
 }
diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs
index 43abfbb1f645e..4b3d1b586b570 100644
--- a/src/libstd/sys/windows/thread.rs
+++ b/src/libstd/sys/windows/thread.rs
@@ -96,4 +96,5 @@ pub mod guard {
     pub type Guard = !;
     pub unsafe fn current() -> Option<Guard> { None }
     pub unsafe fn init() -> Option<Guard> { None }
+    pub unsafe fn deinit() {}
 }
diff --git a/src/libstd/sys_common/thread_info.rs b/src/libstd/sys_common/thread_info.rs
index 6a2b6742367a5..d75cbded7347b 100644
--- a/src/libstd/sys_common/thread_info.rs
+++ b/src/libstd/sys_common/thread_info.rs
@@ -50,3 +50,7 @@ pub fn set(stack_guard: Option<Guard>, thread: Thread) {
         thread,
     }));
 }
+
+pub fn reset_guard(stack_guard: Option<Guard>) {
+    THREAD_INFO.with(move |c| c.borrow_mut().as_mut().unwrap().stack_guard = stack_guard);
+}
diff --git a/src/rustc/rustc.rs b/src/rustc/rustc.rs
index bfd01146d2c46..9fa33f911a168 100644
--- a/src/rustc/rustc.rs
+++ b/src/rustc/rustc.rs
@@ -9,6 +9,16 @@
 // except according to those terms.
 
 #![feature(rustc_private)]
+#![feature(link_args)]
+
+// Set the stack size at link time on Windows. See rustc_driver::in_rustc_thread
+// for the rationale.
+#[cfg_attr(all(windows, target_env = "msvc"), link_args = "/STACK:16777216")]
+// We only build for msvc and gnu now, but we use a exhaustive condition here
+// so we can expect either the stack size to be set or the build fails.
+#[cfg_attr(all(windows, not(target_env = "msvc")), link_args = "-Wl,--stack,16777216")]
+// Also, don't forget to set this for rustdoc.
+extern {}
 
 extern crate rustc_driver;
 
diff --git a/src/tools/rustdoc/main.rs b/src/tools/rustdoc/main.rs
index 9c37e249ba8cc..e726dea84f103 100644
--- a/src/tools/rustdoc/main.rs
+++ b/src/tools/rustdoc/main.rs
@@ -8,6 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(link_args)]
+// Set the stack size at link time on Windows. See rustc_driver::in_rustc_thread
+// for the rationale.
+#[cfg_attr(all(windows, target_env = "msvc"), link_args = "/STACK:16777216")]
+// We only build for msvc and gnu now, but we use a exhaustive condition here
+// so we can expect either the stack size to be set or the build fails.
+#[cfg_attr(all(windows, not(target_env = "msvc")), link_args = "-Wl,--stack,16777216")]
+// See src/rustc/rustc.rs for the corresponding rustc settings.
+extern {}
+
 extern crate rustdoc;
 
 fn main() { rustdoc::main() }