]> git.lizzy.rs Git - rust.git/commitdiff
auto merge of #9834 : alexcrichton/rust/morestack, r=brson
authorbors <bors@rust-lang.org>
Sat, 19 Oct 2013 16:46:18 +0000 (09:46 -0700)
committerbors <bors@rust-lang.org>
Sat, 19 Oct 2013 16:46:18 +0000 (09:46 -0700)
This commit re-introduces the functionality of __morestack in a way that it was
not originally anticipated. Rust does not currently have segmented stacks,
rather just large stack segments. We do not detect when these stack segments are
overrun currently, but this commit leverages __morestack in order to check this.

This commit purges a lot of the old __morestack and stack limit C++
functionality, migrating the necessary chunks to rust. The stack limit is now
entirely maintained in rust, and the "main logic bits" of __morestack are now
also implemented in rust as well.

I put my best effort into validating that this currently builds and runs successfully on osx and linux 32/64 bit, but I was unable to get this working on windows. We never did have unwinding through __morestack frames, and although I tried poking at it for a bit, I was unable to understand why we don't get unwinding right now.

A focus of this commit is to implement as much of the logic in rust as possible. This involved some liberal usage of `no_split_stack` in various locations, along with some use of the `asm!` macro (scary). I modified a bit of C++ to stop calling `record_sp_limit` because this is no longer defined in C++, rather in rust.

Another consequence of this commit is that `thread_local_storage::{get, set}` must both be flagged with `#[rust_stack]`. I've briefly looked at the implementations on osx/linux/windows to ensure that they're pretty small stacks, and I'm pretty sure that they're definitely less than 20K stacks, so we probably don't have a lot to worry about.

Other things worthy of note:
* The default stack size is now 4MB instead of 2MB. This is so that when we request 2MB to call a C function you don't immediately overflow because you have consumed any stack at all.
* `asm!` is actually pretty cool, maybe we could actually define context switching with it?
* I wanted to add links to the internet about all this jazz of storing information in TLS, but I was only able to find a link for the windows implementation. Otherwise my suggestion is just "disassemble on that arch and see what happens"
* I put my best effort forward on arm/mips to tweak __morestack correctly, we have no ability to test this so an extra set of eyes would be useful on these spots.
* This is all really tricky stuff, so I tried to put as many comments as I thought were necessary, but if anything is still unclear (or I completely forgot to take something into account), I'm willing to write more!

1  2 
mk/rt.mk
src/libstd/rt/context.rs
src/libstd/rt/sched.rs
src/libstd/rt/task.rs

diff --cc mk/rt.mk
index f210b5ab3899c46f637ea63ebf15a722d999d1b0,8a11d3553f45840d6127d5574a4709600da9f77d..2fb325e834881b4661c18c78930424dd1b25d1f3
+++ b/mk/rt.mk
@@@ -140,15 -137,15 +137,15 @@@ $$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.
        @$$(call E, compile: $$@)
        $$(Q)$$(call CFG_ASSEMBLE_$(1),$$@,$$<)
  
- $$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/libmorestack.a: $$(MORESTACK_OBJ_$(1)_$(2))
+ $$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/libmorestack.a: $$(MORESTACK_OBJS_$(1)_$(2))
        @$$(call E, link: $$@)
-       $$(Q)$(AR_$(1)) rcs $$@ $$<
+       $$(Q)$(AR_$(1)) rcs $$@ $$^
  
  $$(RT_BUILD_DIR_$(1)_$(2))/$(CFG_RUNTIME_$(1)): $$(RUNTIME_OBJS_$(1)_$(2)) $$(MKFILE_DEPS) \
 -                        $$(RUNTIME_DEF_$(1)_$(2)) $$(LIBUV_LIB_$(1)) $$(JEMALLOC_LIB_$(1))
 +                        $$(RUNTIME_DEF_$(1)_$(2)) $$(LIBUV_LIB_$(1))
        @$$(call E, link: $$@)
        $$(Q)$$(call CFG_LINK_CXX_$(1),$$@, $$(RUNTIME_OBJS_$(1)_$(2)) \
 -          $$(JEMALLOC_LIB_$(1)) $$(CFG_GCCISH_POST_LIB_FLAGS_$(1)) $$(LIBUV_LIB_$(1)) \
 +          $$(LIBUV_LIB_$(1)) \
            $$(CFG_LIBUV_LINK_FLAGS_$(1)),$$(RUNTIME_DEF_$(1)_$(2)),$$(CFG_RUNTIME_$(1)))
  
  # These could go in rt.mk or rustllvm.mk, they're needed for both.
index 222f9a44b1745070dd13e49bf2c8530e230f0d1c,f4616a8e18373ff6f6f1327c29231ed36b791019..7f7545ca230ff5edcb6dbf5d297e04abc200e7d6
@@@ -233,6 -277,185 +277,185 @@@ fn align_down(sp: *mut uint) -> *mut ui
  // ptr::mut_offset is positive ints only
  #[inline]
  pub fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T {
 -    use std::sys::size_of;
 +    use mem::size_of;
      (ptr as int + count * (size_of::<T>() as int)) as *mut T
  }
+ #[inline(always)]
+ pub unsafe fn record_stack_bounds(stack_lo: uint, stack_hi: uint) {
+     // When the old runtime had segmented stacks, it used a calculation that was
+     // "limit + RED_ZONE + FUDGE". The red zone was for things like dynamic
+     // symbol resolution, llvm function calls, etc. In theory this red zone
+     // value is 0, but it matters far less when we have gigantic stacks because
+     // we don't need to be so exact about our stack budget. The "fudge factor"
+     // was because LLVM doesn't emit a stack check for functions < 256 bytes in
+     // size. Again though, we have giant stacks, so we round all these
+     // calculations up to the nice round number of 20k.
+     record_sp_limit(stack_lo + RED_ZONE);
+     return target_record_stack_bounds(stack_lo, stack_hi);
+     #[cfg(not(windows))] #[cfg(not(target_arch = "x86_64"))] #[inline(always)]
+     unsafe fn target_record_stack_bounds(_stack_lo: uint, _stack_hi: uint) {}
+     #[cfg(windows, target_arch = "x86_64")] #[inline(always)]
+     unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) {
+         // Windows compiles C functions which may check the stack bounds. This
+         // means that if we want to perform valid FFI on windows, then we need
+         // to ensure that the stack bounds are what they truly are for this
+         // task. More info can be found at:
+         //   https://github.com/mozilla/rust/issues/3445#issuecomment-26114839
+         //
+         // stack range is at TIB: %gs:0x08 (top) and %gs:0x10 (bottom)
+         asm!("mov $0, %gs:0x08" :: "r"(stack_lo) :: "volatile");
+         asm!("mov $0, %gs:0x10" :: "r"(stack_hi) :: "volatile");
+     }
+ }
+ /// Records the current limit of the stack as specified by `end`.
+ ///
+ /// This is stored in an OS-dependent location, likely inside of the thread
+ /// local storage. The location that the limit is stored is a pre-ordained
+ /// location because it's where LLVM has emitted code to check.
+ ///
+ /// Note that this cannot be called under normal circumstances. This function is
+ /// changing the stack limit, so upon returning any further function calls will
+ /// possibly be triggering the morestack logic if you're not careful.
+ ///
+ /// Also note that this and all of the inside functions are all flagged as
+ /// "inline(always)" because they're messing around with the stack limits.  This
+ /// would be unfortunate for the functions themselves to trigger a morestack
+ /// invocation (if they were an actual function call).
+ #[inline(always)]
+ pub unsafe fn record_sp_limit(limit: uint) {
+     return target_record_sp_limit(limit);
+     // x86-64
+     #[cfg(target_arch = "x86_64", target_os = "macos")] #[inline(always)]
+     unsafe fn target_record_sp_limit(limit: uint) {
+         asm!("movq $$0x60+90*8, %rsi
+               movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
+     }
+     #[cfg(target_arch = "x86_64", target_os = "linux")] #[inline(always)]
+     unsafe fn target_record_sp_limit(limit: uint) {
+         asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
+     }
+     #[cfg(target_arch = "x86_64", target_os = "win32")] #[inline(always)]
+     unsafe fn target_record_sp_limit(limit: uint) {
+         // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
+         // store this inside of the "arbitrary data slot", but double the size
+         // because this is 64 bit instead of 32 bit
+         asm!("movq $0, %gs:0x28" :: "r"(limit) :: "volatile")
+     }
+     #[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
+     unsafe fn target_record_sp_limit(limit: uint) {
+         asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
+     }
+     // x86
+     #[cfg(target_arch = "x86", target_os = "macos")] #[inline(always)]
+     unsafe fn target_record_sp_limit(limit: uint) {
+         asm!("movl $$0x48+90*4, %eax
+               movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
+     }
+     #[cfg(target_arch = "x86", target_os = "linux")]
+     #[cfg(target_arch = "x86", target_os = "freebsd")] #[inline(always)]
+     unsafe fn target_record_sp_limit(limit: uint) {
+         asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
+     }
+     #[cfg(target_arch = "x86", target_os = "win32")] #[inline(always)]
+     unsafe fn target_record_sp_limit(limit: uint) {
+         // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
+         // store this inside of the "arbitrary data slot"
+         asm!("movl $0, %fs:0x14" :: "r"(limit) :: "volatile")
+     }
+     // mips, arm - Some brave soul can port these to inline asm, but it's over
+     //             my head personally
+     #[cfg(target_arch = "mips")]
+     #[cfg(target_arch = "arm")] #[inline(always)]
+     unsafe fn target_record_sp_limit(limit: uint) {
+         return record_sp_limit(limit as *c_void);
+         extern {
+             #[rust_stack]
+             fn record_sp_limit(limit: *c_void);
+         }
+     }
+ }
+ /// The counterpart of the function above, this function will fetch the current
+ /// stack limit stored in TLS.
+ ///
+ /// Note that all of these functions are meant to be exact counterparts of their
+ /// brethren above, except that the operands are reversed.
+ ///
+ /// As with the setter, this function does not have a __morestack header and can
+ /// therefore be called in a "we're out of stack" situation.
+ #[inline(always)]
+ // NOTE: after the next snapshot, can remove the initialization before inline
+ //       assembly due to an improvement in how it's handled, then this specific
+ //       allow directive should get removed.
+ #[allow(dead_assignment)]
+ pub unsafe fn get_sp_limit() -> uint {
+     return target_get_sp_limit();
+     // x86-64
+     #[cfg(target_arch = "x86_64", target_os = "macos")] #[inline(always)]
+     unsafe fn target_get_sp_limit() -> uint {
+         let mut limit: uint = 0;
+         asm!("movq $$0x60+90*8, %rsi
+               movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
+         return limit;
+     }
+     #[cfg(target_arch = "x86_64", target_os = "linux")] #[inline(always)]
+     unsafe fn target_get_sp_limit() -> uint {
+         let mut limit: uint = 0;
+         asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
+         return limit;
+     }
+     #[cfg(target_arch = "x86_64", target_os = "win32")] #[inline(always)]
+     unsafe fn target_get_sp_limit() -> uint {
+         let mut limit: uint = 0;
+         asm!("movq %gs:0x28, $0" : "=r"(limit) ::: "volatile");
+         return limit;
+     }
+     #[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
+     unsafe fn target_get_sp_limit() -> uint {
+         let mut limit: uint = 0;
+         asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
+         return limit;
+     }
+     // x86
+     #[cfg(target_arch = "x86", target_os = "macos")] #[inline(always)]
+     unsafe fn target_get_sp_limit() -> uint {
+         let mut limit: uint = 0;
+         asm!("movl $$0x48+90*4, %eax
+               movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
+         return limit;
+     }
+     #[cfg(target_arch = "x86", target_os = "linux")]
+     #[cfg(target_arch = "x86", target_os = "freebsd")] #[inline(always)]
+     unsafe fn target_get_sp_limit() -> uint {
+         let mut limit: uint = 0;
+         asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
+         return limit;
+     }
+     #[cfg(target_arch = "x86", target_os = "win32")] #[inline(always)]
+     unsafe fn target_get_sp_limit() -> uint {
+         let mut limit: uint = 0;
+         asm!("movl %fs:0x14, $0" : "=r"(limit) ::: "volatile");
+         return limit;
+     }
+     // mips, arm - Some brave soul can port these to inline asm, but it's over
+     //             my head personally
+     #[cfg(target_arch = "mips")]
+     #[cfg(target_arch = "arm")] #[inline(always)]
+     unsafe fn target_get_sp_limit() -> uint {
+         return get_sp_limit() as uint;
+         extern {
+             #[rust_stack]
+             fn get_sp_limit() -> *c_void;
+         }
+     }
+ }
Simple merge
Simple merge