Skip to content

Commit 8b0ec1d

Browse files
committed
Pack generator inputs and outputs into usize if possible
1 parent 3a39e95 commit 8b0ec1d

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

src/generator.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,36 @@ use stack;
1919
use debug;
2020
use arch::{self, StackPointer};
2121

22+
// Wrapper to prevent the compiler from automatically dropping a value when it
23+
// goes out of scope. This is particularly useful when dealing with unwinding
24+
// since mem::forget won't be executed when unwinding.
25+
#[allow(unions_with_drop_fields)]
26+
union NoDrop<T> {
27+
inner: T,
28+
}
29+
30+
// Try to pack a value into a usize if it fits, otherwise pass its address as a usize.
31+
unsafe fn encode_usize<T>(val: &NoDrop<T>) -> usize {
32+
if mem::size_of::<T>() <= mem::size_of::<usize>() &&
33+
mem::align_of::<T>() <= mem::align_of::<usize>() {
34+
let mut out = 0;
35+
ptr::copy_nonoverlapping(&val.inner, &mut out as *mut usize as *mut T, 1);
36+
out
37+
} else {
38+
&val.inner as *const T as usize
39+
}
40+
}
41+
42+
// Unpack a usize produced by encode_usize.
43+
unsafe fn decode_usize<T>(val: usize) -> T {
44+
if mem::size_of::<T>() <= mem::size_of::<usize>() &&
45+
mem::align_of::<T>() <= mem::align_of::<usize>() {
46+
ptr::read(&val as *const usize as *const T)
47+
} else {
48+
ptr::read(val as *const T)
49+
}
50+
}
51+
2252
#[derive(Debug, Clone, Copy)]
2353
pub enum State {
2454
/// Generator can be resumed. This is the initial state.
@@ -114,10 +144,10 @@ impl<'a, Input, Output, Stack> Generator<'a, Input, Output, Stack>
114144
where Input: Send, Output: Send, Stack: stack::Stack,
115145
F: FnOnce(&Yielder<Input, Output>, Input) {
116146
// Retrieve our environment from the callee and return control to it.
117-
let f = ptr::read(env as *const F);
147+
let f: F = decode_usize(env);
118148
let (data, stack_ptr) = arch::swap(0, stack_ptr);
119149
// See the second half of Yielder::suspend_bare.
120-
let input = ptr::read(data as *const Input);
150+
let input = decode_usize(data);
121151
// Run the body of the generator.
122152
let yielder = Yielder::new(stack_ptr);
123153
f(&yielder, input);
@@ -127,8 +157,8 @@ impl<'a, Input, Output, Stack> Generator<'a, Input, Output, Stack>
127157
let stack_ptr = arch::init(&stack, generator_wrapper::<Input, Output, Stack, F>);
128158

129159
// Transfer environment to the callee.
130-
let stack_ptr = arch::swap_link(&f as *const F as usize, stack_ptr, &stack).1;
131-
mem::forget(f);
160+
let f2 = NoDrop { inner: f };
161+
let stack_ptr = arch::swap_link(encode_usize(&f2), stack_ptr, &stack).1;
132162

133163
Generator {
134164
stack: stack,
@@ -151,13 +181,13 @@ impl<'a, Input, Output, Stack> Generator<'a, Input, Output, Stack>
151181

152182
// Switch to the generator function, and retrieve the yielded value.
153183
unsafe {
154-
let (data_out, stack_ptr) = arch::swap_link(&input as *const Input as usize, stack_ptr, &self.stack);
184+
let input2 = NoDrop { inner: input };
185+
let (data_out, stack_ptr) = arch::swap_link(encode_usize(&input2), stack_ptr, &self.stack);
155186
self.stack_ptr = stack_ptr;
156-
mem::forget(input);
157187

158188
// If the generator function has finished, return None, otherwise return the
159189
// yielded value.
160-
stack_ptr.map(|_| ptr::read(data_out as *const Output))
190+
stack_ptr.map(|_| decode_usize(data_out))
161191
}
162192
})
163193
}
@@ -201,10 +231,10 @@ impl<Input, Output> Yielder<Input, Output>
201231
#[inline(always)]
202232
pub fn suspend(&self, item: Output) -> Input {
203233
unsafe {
204-
let (data, stack_ptr) = arch::swap(&item as *const Output as usize, self.stack_ptr.get());
205-
mem::forget(item);
234+
let item2 = NoDrop { inner: item };
235+
let (data, stack_ptr) = arch::swap(encode_usize(&item2), self.stack_ptr.get());
206236
self.stack_ptr.set(stack_ptr);
207-
ptr::read(data as *const Input)
237+
decode_usize(data)
208238
}
209239
}
210240
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
55
// http://opensource.org/licenses/MIT>, at your option. This file may not be
66
// copied, modified, or distributed except according to those terms.
7-
#![feature(asm, naked_functions, cfg_target_vendor, nonzero)]
7+
#![feature(asm, naked_functions, cfg_target_vendor, nonzero, untagged_unions)]
88
#![cfg_attr(feature = "alloc", feature(alloc, heap_api))]
99
#![cfg_attr(test, feature(test))]
1010
#![no_std]

0 commit comments

Comments
 (0)