]> git.lizzy.rs Git - rust.git/blob - src/tls.rs
rustup for big refactor; kill most of validation
[rust.git] / src / tls.rs
1 use rustc::{ty, mir};
2
3 use super::{TlsKey, TlsEntry, EvalResult, EvalErrorKind, Scalar, ScalarExt, Memory, Evaluator,
4             Place, StackPopCleanup, EvalContext};
5
6 pub trait MemoryExt<'tcx> {
7     fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey;
8     fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx>;
9     fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar>;
10     fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx>;
11     fn fetch_tls_dtor(
12         &mut self,
13         key: Option<TlsKey>,
14     ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)>;
15 }
16
17 pub trait EvalContextExt<'tcx> {
18     fn run_tls_dtors(&mut self) -> EvalResult<'tcx>;
19 }
20
21 impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evaluator<'tcx>> {
22     fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey {
23         let new_key = self.data.next_thread_local;
24         self.data.next_thread_local += 1;
25         let ptr_size = self.pointer_size();
26         self.data.thread_local.insert(
27             new_key,
28             TlsEntry {
29                 data: Scalar::null(ptr_size).into(),
30                 dtor,
31             },
32         );
33         trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
34         new_key
35     }
36
37     fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> {
38         match self.data.thread_local.remove(&key) {
39             Some(_) => {
40                 trace!("TLS key {} removed", key);
41                 Ok(())
42             }
43             None => err!(TlsOutOfBounds),
44         }
45     }
46
47     fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar> {
48         match self.data.thread_local.get(&key) {
49             Some(&TlsEntry { data, .. }) => {
50                 trace!("TLS key {} loaded: {:?}", key, data);
51                 Ok(data)
52             }
53             None => err!(TlsOutOfBounds),
54         }
55     }
56
57     fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx> {
58         match self.data.thread_local.get_mut(&key) {
59             Some(&mut TlsEntry { ref mut data, .. }) => {
60                 trace!("TLS key {} stored: {:?}", key, new_data);
61                 *data = new_data;
62                 Ok(())
63             }
64             None => err!(TlsOutOfBounds),
65         }
66     }
67
68     /// Returns a dtor, its argument and its index, if one is supposed to run
69     ///
70     /// An optional destructor function may be associated with each key value.
71     /// At thread exit, if a key value has a non-NULL destructor pointer,
72     /// and the thread has a non-NULL value associated with that key,
73     /// the value of the key is set to NULL, and then the function pointed
74     /// to is called with the previously associated value as its sole argument.
75     /// The order of destructor calls is unspecified if more than one destructor
76     /// exists for a thread when it exits.
77     ///
78     /// If, after all the destructors have been called for all non-NULL values
79     /// with associated destructors, there are still some non-NULL values with
80     /// associated destructors, then the process is repeated.
81     /// If, after at least {PTHREAD_DESTRUCTOR_ITERATIONS} iterations of destructor
82     /// calls for outstanding non-NULL values, there are still some non-NULL values
83     /// with associated destructors, implementations may stop calling destructors,
84     /// or they may continue calling destructors until no non-NULL values with
85     /// associated destructors exist, even though this might result in an infinite loop.
86     fn fetch_tls_dtor(
87         &mut self,
88         key: Option<TlsKey>,
89     ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> {
90         use std::collections::Bound::*;
91
92         let ptr_size = self.pointer_size();
93         let thread_local = &mut self.data.thread_local;
94         let start = match key {
95             Some(key) => Excluded(key),
96             None => Unbounded,
97         };
98         for (&key, &mut TlsEntry { ref mut data, dtor }) in
99             thread_local.range_mut((start, Unbounded))
100         {
101             if !data.is_null() {
102                 if let Some(dtor) = dtor {
103                     let ret = Some((dtor, *data, key));
104                     *data = Scalar::null(ptr_size);
105                     return ret;
106                 }
107             }
108         }
109         None
110     }
111 }
112
113 impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>> {
114     fn run_tls_dtors(&mut self) -> EvalResult<'tcx> {
115         let mut dtor = self.memory.fetch_tls_dtor(None);
116         // FIXME: replace loop by some structure that works with stepping
117         while let Some((instance, ptr, key)) = dtor {
118             trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
119             // TODO: Potentially, this has to support all the other possible instances?
120             // See eval_fn_call in interpret/terminator/mod.rs
121             let mir = self.load_mir(instance.def)?;
122             let ret = Place::null(&self);
123             self.push_stack_frame(
124                 instance,
125                 mir.span,
126                 mir,
127                 ret,
128                 StackPopCleanup::None,
129             )?;
130             let arg_local = self.frame().mir.args_iter().next().ok_or_else(
131                 || EvalErrorKind::AbiViolation("TLS dtor does not take enough arguments.".to_owned()),
132             )?;
133             let dest = self.eval_place(&mir::Place::Local(arg_local))?;
134             self.write_scalar(ptr, dest)?;
135
136             // step until out of stackframes
137             while self.step()? {}
138
139             dtor = match self.memory.fetch_tls_dtor(Some(key)) {
140                 dtor @ Some(_) => dtor,
141                 None => self.memory.fetch_tls_dtor(None),
142             };
143         }
144         // FIXME: On a windows target, call `unsafe extern "system" fn on_tls_callback`.
145         Ok(())
146     }
147 }