Skip to content

Commit 7755ffd

Browse files
committed
Remove #[fixed_stack_segment] and #[rust_stack]
These two attributes are no longer useful now that Rust has decided to leave segmented stacks behind. It is assumed that the rust task's stack is always large enough to make an FFI call (due to the stack being very large). There's always the case of stack overflow, however, to consider. This does not change the behavior of stack overflow in Rust. This is still normally triggered by the __morestack function and aborts the whole process. C stack overflow will continue to corrupt the stack, however (as it did before this commit as well). The future improvement of a guard page at the end of every rust stack is still unimplemented and is intended to be the mechanism through which we attempt to detect C stack overflow. Closes #8822 Closes #10155
1 parent 4059b5c commit 7755ffd

File tree

111 files changed

+261
-1047
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+261
-1047
lines changed

doc/tutorial-ffi.md

+25-143
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ extern {
1919
fn snappy_max_compressed_length(source_length: size_t) -> size_t;
2020
}
2121
22-
#[fixed_stack_segment]
2322
fn main() {
2423
let x = unsafe { snappy_max_compressed_length(100) };
25-
println(fmt!("max compressed length of a 100 byte buffer: %?", x));
24+
println!("max compressed length of a 100 byte buffer: {}", x);
2625
}
2726
~~~~
2827

@@ -36,11 +35,6 @@ interfaces that aren't thread-safe, and almost any function that takes a pointer
3635
valid for all possible inputs since the pointer could be dangling, and raw pointers fall outside of
3736
Rust's safe memory model.
3837

39-
Finally, the `#[fixed_stack_segment]` annotation that appears on
40-
`main()` instructs the Rust compiler that when `main()` executes, it
41-
should request a "very large" stack segment. More details on
42-
stack management can be found in the following sections.
43-
4438
When declaring the argument types to a foreign function, the Rust compiler will not check if the
4539
declaration is correct, so specifying it correctly is part of keeping the binding correct at
4640
runtime.
@@ -81,8 +75,6 @@ length is number of elements currently contained, and the capacity is the total
8175
the allocated memory. The length is less than or equal to the capacity.
8276

8377
~~~~ {.xfail-test}
84-
#[fixed_stack_segment]
85-
#[inline(never)]
8678
pub fn validate_compressed_buffer(src: &[u8]) -> bool {
8779
unsafe {
8880
snappy_validate_compressed_buffer(vec::raw::to_ptr(src), src.len() as size_t) == 0
@@ -94,36 +86,6 @@ The `validate_compressed_buffer` wrapper above makes use of an `unsafe` block, b
9486
guarantee that calling it is safe for all inputs by leaving off `unsafe` from the function
9587
signature.
9688

97-
The `validate_compressed_buffer` wrapper is also annotated with two
98-
attributes `#[fixed_stack_segment]` and `#[inline(never)]`. The
99-
purpose of these attributes is to guarantee that there will be
100-
sufficient stack for the C function to execute. This is necessary
101-
because Rust, unlike C, does not assume that the stack is allocated in
102-
one continuous chunk. Instead, we rely on a *segmented stack* scheme,
103-
in which the stack grows and shrinks as necessary. C code, however,
104-
expects one large stack, and so callers of C functions must request a
105-
large stack segment to ensure that the C routine will not run off the
106-
end of the stack.
107-
108-
The compiler includes a lint mode that will report an error if you
109-
call a C function without a `#[fixed_stack_segment]` attribute. More
110-
details on the lint mode are given in a later section.
111-
112-
You may be wondering why we include a `#[inline(never)]` directive.
113-
This directive informs the compiler never to inline this function.
114-
While not strictly necessary, it is usually a good idea to use an
115-
`#[inline(never)]` directive in concert with `#[fixed_stack_segment]`.
116-
The reason is that if a fn annotated with `fixed_stack_segment` is
117-
inlined, then its caller also inherits the `fixed_stack_segment`
118-
annotation. This means that rather than requesting a large stack
119-
segment only for the duration of the call into C, the large stack
120-
segment would be used for the entire duration of the caller. This is
121-
not necessarily *bad* -- it can for example be more efficient,
122-
particularly if `validate_compressed_buffer()` is called multiple
123-
times in a row -- but it does work against the purpose of the
124-
segmented stack scheme, which is to keep stacks small and thus
125-
conserve address space.
126-
12789
The `snappy_compress` and `snappy_uncompress` functions are more complex, since a buffer has to be
12890
allocated to hold the output too.
12991

@@ -134,8 +96,6 @@ the true length after compression for setting the length.
13496

13597
~~~~ {.xfail-test}
13698
pub fn compress(src: &[u8]) -> ~[u8] {
137-
#[fixed_stack_segment]; #[inline(never)];
138-
13999
unsafe {
140100
let srclen = src.len() as size_t;
141101
let psrc = vec::raw::to_ptr(src);
@@ -156,8 +116,6 @@ format and `snappy_uncompressed_length` will retrieve the exact buffer size requ
156116

157117
~~~~ {.xfail-test}
158118
pub fn uncompress(src: &[u8]) -> Option<~[u8]> {
159-
#[fixed_stack_segment]; #[inline(never)];
160-
161119
unsafe {
162120
let srclen = src.len() as size_t;
163121
let psrc = vec::raw::to_ptr(src);
@@ -181,98 +139,28 @@ pub fn uncompress(src: &[u8]) -> Option<~[u8]> {
181139
For reference, the examples used here are also available as an [library on
182140
GitHub](https://github.com/thestinger/rust-snappy).
183141

184-
# Automatic wrappers
185-
186-
Sometimes writing Rust wrappers can be quite tedious. For example, if
187-
function does not take any pointer arguments, often there is no need
188-
for translating types. In such cases, it is usually still a good idea
189-
to have a Rust wrapper so as to manage the segmented stacks, but you
190-
can take advantage of the (standard) `externfn!` macro to remove some
191-
of the tedium.
192-
193-
In the initial section, we showed an extern block that added a call
194-
to a specific snappy API:
195-
196-
~~~~ {.xfail-test}
197-
use std::libc::size_t;
198-
199-
#[link_args = "-lsnappy"]
200-
extern {
201-
fn snappy_max_compressed_length(source_length: size_t) -> size_t;
202-
}
203-
204-
#[fixed_stack_segment]
205-
fn main() {
206-
let x = unsafe { snappy_max_compressed_length(100) };
207-
println(fmt!("max compressed length of a 100 byte buffer: %?", x));
208-
}
209-
~~~~
210-
211-
To avoid the need to create a wrapper fn for `snappy_max_compressed_length()`,
212-
and also to avoid the need to think about `#[fixed_stack_segment]`, we
213-
could simply use the `externfn!` macro instead, as shown here:
214-
215-
~~~~ {.xfail-test}
216-
use std::libc::size_t;
217-
218-
externfn!(#[link_args = "-lsnappy"]
219-
fn snappy_max_compressed_length(source_length: size_t) -> size_t)
220-
221-
fn main() {
222-
let x = unsafe { snappy_max_compressed_length(100) };
223-
println(fmt!("max compressed length of a 100 byte buffer: %?", x));
224-
}
225-
~~~~
226-
227-
As you can see from the example, `externfn!` replaces the extern block
228-
entirely. After macro expansion, it will create something like this:
229-
230-
~~~~ {.xfail-test}
231-
use std::libc::size_t;
232-
233-
// Automatically generated by
234-
// externfn!(#[link_args = "-lsnappy"]
235-
// fn snappy_max_compressed_length(source_length: size_t) -> size_t)
236-
unsafe fn snappy_max_compressed_length(source_length: size_t) -> size_t {
237-
#[fixed_stack_segment]; #[inline(never)];
238-
return snappy_max_compressed_length(source_length);
239-
240-
#[link_args = "-lsnappy"]
241-
extern {
242-
fn snappy_max_compressed_length(source_length: size_t) -> size_t;
243-
}
244-
}
245-
246-
fn main() {
247-
let x = unsafe { snappy_max_compressed_length(100) };
248-
println(fmt!("max compressed length of a 100 byte buffer: %?", x));
249-
}
250-
~~~~
251-
252-
# Segmented stacks and the linter
253-
254-
By default, whenever you invoke a non-Rust fn, the `cstack` lint will
255-
check that one of the following conditions holds:
256-
257-
1. The call occurs inside of a fn that has been annotated with
258-
`#[fixed_stack_segment]`;
259-
2. The call occurs inside of an `extern fn`;
260-
3. The call occurs within a stack closure created by some other
261-
safe fn.
262-
263-
All of these conditions ensure that you are running on a large stack
264-
segment. However, they are sometimes too strict. If your application
265-
will be making many calls into C, it is often beneficial to promote
266-
the `#[fixed_stack_segment]` attribute higher up the call chain. For
267-
example, the Rust compiler actually labels main itself as requiring a
268-
`#[fixed_stack_segment]`. In such cases, the linter is just an
269-
annoyance, because all C calls that occur from within the Rust
270-
compiler are made on a large stack. Another situation where this
271-
frequently occurs is on a 64-bit architecture, where large stacks are
272-
the default. In cases, you can disable the linter by including a
273-
`#[allow(cstack)]` directive somewhere, which permits violations of
274-
the "cstack" rules given above (you can also use `#[warn(cstack)]` to
275-
convert the errors into warnings, if you prefer).
142+
# Stack management
143+
144+
Rust tasks by default run on a "large stack". This is actually implemented as a
145+
reserving a large segment of the address space and then lazily mapping in pages
146+
as they are needed. When calling an external C function, the code is invoked on
147+
the same stack as the rust stack. This means that there is no extra
148+
stack-switching mechanism in place because it is assumed that the large stack
149+
for the rust task is plenty for the C function to have.
150+
151+
A planned future improvement (net yet implemented at the time of this writing)
152+
is to have a guard page at the end of every rust stack. No rust function will
153+
hit this guard page (due to rust's usage of LLVM's __morestack). The intention
154+
for this unmapped page is to prevent infinite recursion in C from overflowing
155+
onto other rust stacks. If the guard page is hit, then the process will be
156+
terminated with a message saying that the guard page was hit.
157+
158+
For normal external function usage, this all means that there shouldn't be any
159+
need for any extra effort on a user's perspective. The C stack naturally
160+
interleaves with the rust stack, and it's "large enough" for both to
161+
interoperate. If, however, it is determined that a larger stack is necessary,
162+
there are appropriate functions in the task spawning API to control the size of
163+
the stack of the task which is spawned.
276164

277165
# Destructors
278166

@@ -296,9 +184,6 @@ pub struct Unique<T> {
296184
297185
impl<T: Send> Unique<T> {
298186
pub fn new(value: T) -> Unique<T> {
299-
#[fixed_stack_segment];
300-
#[inline(never)];
301-
302187
unsafe {
303188
let ptr = malloc(std::mem::size_of::<T>() as size_t) as *mut T;
304189
assert!(!ptr::is_null(ptr));
@@ -322,9 +207,6 @@ impl<T: Send> Unique<T> {
322207
#[unsafe_destructor]
323208
impl<T: Send> Drop for Unique<T> {
324209
fn drop(&mut self) {
325-
#[fixed_stack_segment];
326-
#[inline(never)];
327-
328210
unsafe {
329211
let x = intrinsics::init(); // dummy value to swap in
330212
// moving the object out is needed to call the destructor
@@ -384,8 +266,8 @@ extern {
384266
}
385267
386268
fn main() {
387-
println(fmt!("You have readline version %d installed.",
388-
rl_readline_version as int));
269+
println!("You have readline version {} installed.",
270+
rl_readline_version as int);
389271
}
390272
~~~
391273

src/libextra/c_vec.rs

-4
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ mod tests {
162162
}
163163

164164
impl Runnable for LibcFree {
165-
#[fixed_stack_segment]
166165
fn run(~self) {
167166
unsafe {
168167
libc::free(self.mem)
@@ -171,9 +170,6 @@ mod tests {
171170
}
172171

173172
fn malloc(n: size_t) -> CVec<u8> {
174-
#[fixed_stack_segment];
175-
#[inline(never)];
176-
177173
unsafe {
178174
let mem = libc::malloc(n);
179175

src/libextra/flate.rs

-4
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ static TINFL_FLAG_PARSE_ZLIB_HEADER : c_int = 0x1; // parse zlib header and adle
4747
static TDEFL_WRITE_ZLIB_HEADER : c_int = 0x01000; // write zlib header and adler32 checksum
4848

4949
fn deflate_bytes_internal(bytes: &[u8], flags: c_int) -> ~[u8] {
50-
#[fixed_stack_segment]; #[inline(never)];
51-
5250
do bytes.as_imm_buf |b, len| {
5351
unsafe {
5452
let mut outsz : size_t = 0;
@@ -75,8 +73,6 @@ pub fn deflate_bytes_zlib(bytes: &[u8]) -> ~[u8] {
7573
}
7674

7775
fn inflate_bytes_internal(bytes: &[u8], flags: c_int) -> ~[u8] {
78-
#[fixed_stack_segment]; #[inline(never)];
79-
8076
do bytes.as_imm_buf |b, len| {
8177
unsafe {
8278
let mut outsz : size_t = 0;

src/libextra/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ Rust extras are part of the standard Rust distribution.
3838

3939
#[deny(non_camel_case_types)];
4040
#[deny(missing_doc)];
41+
#[allow(unrecognized_lint)]; // NOTE: remove after the next snapshot
42+
#[allow(cstack)]; // NOTE: remove after the next snapshot.
4143

4244
use std::str::{StrSlice, OwnedStr};
4345

src/libextra/test.rs

-2
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,6 @@ fn optgroups() -> ~[getopts::groups::OptGroup] {
220220
}
221221

222222
fn usage(binary: &str, helpstr: &str) {
223-
#[fixed_stack_segment]; #[inline(never)];
224-
225223
let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
226224
println(groups::usage(message, optgroups()));
227225
println("");

src/libextra/time.rs

-13
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ impl Ord for Timespec {
6363
* nanoseconds since 1970-01-01T00:00:00Z.
6464
*/
6565
pub fn get_time() -> Timespec {
66-
#[fixed_stack_segment]; #[inline(never)];
67-
6866
unsafe {
6967
let mut sec = 0i64;
7068
let mut nsec = 0i32;
@@ -79,8 +77,6 @@ pub fn get_time() -> Timespec {
7977
* in nanoseconds since an unspecified epoch.
8078
*/
8179
pub fn precise_time_ns() -> u64 {
82-
#[fixed_stack_segment]; #[inline(never)];
83-
8480
unsafe {
8581
let mut ns = 0u64;
8682
rustrt::precise_time_ns(&mut ns);
@@ -98,8 +94,6 @@ pub fn precise_time_s() -> f64 {
9894
}
9995

10096
pub fn tzset() {
101-
#[fixed_stack_segment]; #[inline(never)];
102-
10397
unsafe {
10498
rustrt::rust_tzset();
10599
}
@@ -143,8 +137,6 @@ pub fn empty_tm() -> Tm {
143137

144138
/// Returns the specified time in UTC
145139
pub fn at_utc(clock: Timespec) -> Tm {
146-
#[fixed_stack_segment]; #[inline(never)];
147-
148140
unsafe {
149141
let Timespec { sec, nsec } = clock;
150142
let mut tm = empty_tm();
@@ -160,8 +152,6 @@ pub fn now_utc() -> Tm {
160152

161153
/// Returns the specified time in the local timezone
162154
pub fn at(clock: Timespec) -> Tm {
163-
#[fixed_stack_segment]; #[inline(never)];
164-
165155
unsafe {
166156
let Timespec { sec, nsec } = clock;
167157
let mut tm = empty_tm();
@@ -179,8 +169,6 @@ pub fn now() -> Tm {
179169
impl Tm {
180170
/// Convert time to the seconds from January 1, 1970
181171
pub fn to_timespec(&self) -> Timespec {
182-
#[fixed_stack_segment]; #[inline(never)];
183-
184172
unsafe {
185173
let sec = match self.tm_gmtoff {
186174
0_i32 => rustrt::rust_timegm(self),
@@ -969,7 +957,6 @@ mod tests {
969957
use std::libc;
970958
971959
#[cfg(windows)]
972-
#[fixed_stack_segment]
973960
fn set_time_zone() {
974961
// Windows crt doesn't see any environment variable set by
975962
// `SetEnvironmentVariable`, which `os::setenv` internally uses.

src/librustc/driver/driver.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,6 @@ pub fn phase_3_run_analysis_passes(sess: Session,
278278
time(time_passes, "loop checking", (), |_|
279279
middle::check_loop::check_crate(ty_cx, crate));
280280

281-
time(time_passes, "stack checking", (), |_|
282-
middle::stack_check::stack_check_crate(ty_cx, crate));
283-
284281
let middle::moves::MoveMaps {moves_map, moved_variables_set,
285282
capture_map} =
286283
time(time_passes, "compute moves", (), |_|
@@ -428,7 +425,6 @@ pub fn stop_after_phase_5(sess: Session) -> bool {
428425
return false;
429426
}
430427

431-
#[fixed_stack_segment]
432428
pub fn compile_input(sess: Session, cfg: ast::CrateConfig, input: &input,
433429
outdir: &Option<Path>, output: &Option<Path>) {
434430
// We need nested scopes here, because the intermediate results can keep
@@ -703,12 +699,7 @@ pub fn build_session_options(binary: @str,
703699
}
704700

705701
if debugging_opts & session::debug_llvm != 0 {
706-
set_llvm_debug();
707-
708-
fn set_llvm_debug() {
709-
#[fixed_stack_segment]; #[inline(never)];
710-
unsafe { llvm::LLVMSetDebug(1); }
711-
}
702+
unsafe { llvm::LLVMSetDebug(1); }
712703
}
713704

714705
let output_type =

0 commit comments

Comments
 (0)