thread.rs 12.2 KB
Newer Older
qinsoon's avatar
qinsoon committed
1
#![allow(dead_code)]
qinsoon's avatar
qinsoon committed
2 3

use ast::ir::*;
qinsoon's avatar
qinsoon committed
4 5 6
use ast::ptr::*;
use ast::types::*;
use vm::VM;
7
use runtime;
8
use runtime::ValueLocation;
9
use runtime::mm;
qinsoon's avatar
qinsoon committed
10

qinsoon's avatar
qinsoon committed
11
use utils::ByteSize;
qinsoon's avatar
qinsoon committed
12
use utils::Address;
qinsoon's avatar
qinsoon committed
13
use utils::Word;
qinsoon's avatar
qinsoon committed
14 15 16
use utils::mem::memmap;
use utils::mem::memsec;

17
use std::mem;
qinsoon's avatar
qinsoon committed
18 19
use std::thread;
use std::thread::JoinHandle;
20
use std::sync::Arc;
qinsoon's avatar
qinsoon committed
21 22 23 24 25 26

pub const STACK_SIZE : ByteSize = (4 << 20); // 4mb

#[cfg(target_arch = "x86_64")]
pub const PAGE_SIZE  : ByteSize = (4 << 10); // 4kb

qinsoon's avatar
qinsoon committed
27 28 29
impl_mu_entity!(MuThread);
impl_mu_entity!(MuStack);

30
#[repr(C)]
qinsoon's avatar
qinsoon committed
31
pub struct MuStack {
qinsoon's avatar
qinsoon committed
32
    pub hdr: MuEntityHeader,
33 34 35

    // address, id
    func: Option<(ValueLocation, MuID)>,
qinsoon's avatar
qinsoon committed
36
    
qinsoon's avatar
qinsoon committed
37
    size: ByteSize,
qinsoon's avatar
qinsoon committed
38 39 40 41 42 43 44 45 46 47
    //    lo addr                                                    hi addr
    //     | overflow guard page | actual stack ..................... | underflow guard page|
    //     |                     |                                    |                     |
    // overflowGuard           lowerBound                           upperBound
    //                                                              underflowGuard    
    overflow_guard : Address,
    lower_bound    : Address,
    upper_bound    : Address,
    underflow_guard: Address,
    
qinsoon's avatar
qinsoon committed
48 49 50 51 52
    // this frame pointers should only be used when stack is not active
    sp : Address,
    bp : Address,
    ip : Address,
    
53
    state: MuStackState,
qinsoon's avatar
qinsoon committed
54
    #[allow(dead_code)]
55
    mmap           : Option<memmap::Mmap>
qinsoon's avatar
qinsoon committed
56 57 58
}

impl MuStack {
59
    pub fn new(id: MuID, func_addr: ValueLocation, func: &MuFunction) -> MuStack {
qinsoon's avatar
qinsoon committed
60 61 62 63 64 65 66
        let total_size = PAGE_SIZE * 2 + STACK_SIZE;
        
        let anon_mmap = match memmap::Mmap::anonymous(total_size, memmap::Protection::ReadWrite) {
            Ok(m) => m,
            Err(_) => panic!("failed to mmap for a stack"),
        };
        
qinsoon's avatar
qinsoon committed
67 68 69 70 71 72 73 74 75 76 77 78
        let mmap_start = Address::from_ptr(anon_mmap.ptr());
        debug_assert!(mmap_start.is_aligned_to(PAGE_SIZE));
        
        let overflow_guard = mmap_start;
        let lower_bound = mmap_start.plus(PAGE_SIZE);
        let upper_bound = lower_bound.plus(STACK_SIZE);
        let underflow_guard = upper_bound;
        
        unsafe {
            memsec::mprotect(overflow_guard.to_ptr_mut::<u8>(),  PAGE_SIZE, memsec::Prot::NoAccess);
            memsec::mprotect(underflow_guard.to_ptr_mut::<u8>(), PAGE_SIZE, memsec::Prot::NoAccess);
        }
qinsoon's avatar
qinsoon committed
79
        
qinsoon's avatar
qinsoon committed
80 81 82 83 84 85 86 87
        debug!("creating stack {} with entry func {:?}", id, func);
        debug!("overflow_guard : {}", overflow_guard);
        debug!("lower_bound    : {}", lower_bound);
        debug!("upper_bound    : {}", upper_bound);
        debug!("underflow_guard: {}", underflow_guard);
        
        MuStack {
            hdr: MuEntityHeader::unnamed(id),
88
            func: Some((func_addr, func.id())),
qinsoon's avatar
qinsoon committed
89 90 91 92 93 94 95 96 97 98 99 100 101
            
            state: MuStackState::Ready(func.sig.arg_tys.clone()),
            
            size: STACK_SIZE,
            overflow_guard: overflow_guard,
            lower_bound: lower_bound,
            upper_bound: upper_bound,
            underflow_guard: upper_bound,
            
            sp: upper_bound,
            bp: upper_bound,
            ip: unsafe {Address::zero()},
            
102
            mmap: Some(anon_mmap)
qinsoon's avatar
qinsoon committed
103
        }
qinsoon's avatar
qinsoon committed
104
    }
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    
    #[cfg(target_arch = "x86_64")]
    pub fn runtime_load_args(&mut self, vals: Vec<ValueLocation>) {
        use compiler::backend::Word;
        use compiler::backend::WORD_SIZE;
        use compiler::backend::RegGroup;
        use compiler::backend::x86_64;
        
        let mut gpr_used = vec![];
        let mut fpr_used = vec![];
        
        for i in 0..vals.len() {
            let ref val = vals[i];
            let (reg_group, word) = val.load_value();
            
            match reg_group {
                RegGroup::GPR => gpr_used.push(word),
                RegGroup::FPR => fpr_used.push(word),
            }
        }
        
        let mut stack_ptr = self.sp;
        for i in 0..x86_64::ARGUMENT_FPRs.len() {
            stack_ptr = stack_ptr.sub(WORD_SIZE);
            let val = {
                if i < fpr_used.len() {
                    fpr_used[i]
                } else {
                    0 as Word
                }
            };
            
            debug!("store {} to {}", val, stack_ptr);
            unsafe {stack_ptr.store(val);}
        }
        
        for i in 0..x86_64::ARGUMENT_GPRs.len() {
            stack_ptr = stack_ptr.sub(WORD_SIZE);
            let val = {
                if i < gpr_used.len() {
                    gpr_used[i]
                } else {
                    0 as Word
                }
            };
            
            debug!("store {} to {}", val, stack_ptr);
            unsafe {stack_ptr.store(val);}
        }
qinsoon's avatar
qinsoon committed
154

155 156
        // save it back
        self.sp = stack_ptr;
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        
        self.print_stack(Some(20));
    }
    
    pub fn print_stack(&self, n_entries: Option<usize>) {
        use compiler::backend::Word;
        use compiler::backend::WORD_SIZE;
        
        let mut cursor = self.upper_bound.sub(WORD_SIZE);
        let mut count = 0;
        
        println!("0x{:x} | UPPER_BOUND", self.upper_bound); 
        while cursor >= self.lower_bound {
            let val = unsafe{cursor.load::<Word>()};
            print!("0x{:x} | 0x{:x} ({})", cursor, val, val);
            
            if cursor == self.sp {
                print!(" <- SP");
            }
            
            println!("");
            
            cursor = cursor.sub(WORD_SIZE);
            count += 1;
            
            if n_entries.is_some() && count > n_entries.unwrap() {
                println!("...");
                break;
            }
        }
        
        println!("0x{:x} | LOWER_BOUND", self.lower_bound); 
189
    }
qinsoon's avatar
qinsoon committed
190 191
}

qinsoon's avatar
qinsoon committed
192 193 194 195 196 197
pub enum MuStackState {
    Ready(Vec<P<MuType>>), // ready to resume when values of given types are supplied (can be empty)
    Active,
    Dead
}

198
#[repr(C)]
199
#[allow(improper_ctypes)]
200
// do not change the layout (unless change the offset of fields correspondingly)
qinsoon's avatar
qinsoon committed
201
pub struct MuThread {
qinsoon's avatar
qinsoon committed
202
    pub hdr: MuEntityHeader,
203
    allocator: mm::Mutator,
qinsoon's avatar
qinsoon committed
204
    pub stack: Option<Box<MuStack>>,
qinsoon's avatar
qinsoon committed
205
    
206
    native_sp_loc: Address,
207 208
    user_tls: Option<Address>,
    
qinsoon's avatar
qinsoon committed
209 210
    pub vm: Arc<VM>,
    pub exception_obj: Address
qinsoon's avatar
qinsoon committed
211 212
}

213 214 215 216 217
// this depends on the layout of MuThread
lazy_static! {
    pub static ref NATIVE_SP_LOC_OFFSET : usize = mem::size_of::<MuEntityHeader>() 
                + mem::size_of::<Box<mm::Mutator>>()
                + mem::size_of::<Option<Box<MuStack>>>();
qinsoon's avatar
qinsoon committed
218 219
    
    pub static ref ALLOCATOR_OFFSET : usize = mem::size_of::<MuEntityHeader>();
220 221 222 223 224 225
    
    pub static ref VM_OFFSET : usize = mem::size_of::<MuEntityHeader>() 
                + mem::size_of::<Box<mm::Mutator>>()
                + mem::size_of::<Option<Box<MuStack>>>()
                + mem::size_of::<Address>()
                + mem::size_of::<Option<Address>>();
qinsoon's avatar
qinsoon committed
226 227

    pub static ref EXCEPTION_OBJ_OFFSET : usize = *VM_OFFSET + mem::size_of::<Arc<VM>>();                
228 229
}

230
#[cfg(target_arch = "x86_64")]
Kunshan Wang's avatar
Kunshan Wang committed
231
#[cfg(any(target_os = "macos", target_os = "linux"))]
232 233
#[link(name = "runtime")]
extern "C" {
qinsoon's avatar
qinsoon committed
234
    pub fn set_thread_local(thread: *mut MuThread);
qinsoon's avatar
qinsoon committed
235
    pub fn muentry_get_thread_local() -> Address;
236 237 238
}

#[cfg(target_arch = "x86_64")]
Kunshan Wang's avatar
Kunshan Wang committed
239
#[cfg(any(target_os = "macos", target_os = "linux"))]
240 241 242
#[link(name = "swap_stack")]
extern "C" {
    fn swap_to_mu_stack(new_sp: Address, entry: Address, old_sp_loc: Address);
243
    fn fake_swap_mu_thread(old_sp_loc: Address);
qinsoon's avatar
qinsoon committed
244
    fn muentry_swap_back_to_native_stack(sp_loc: Address);
qinsoon's avatar
qinsoon committed
245
    pub fn get_current_frame_rbp() -> Address;
qinsoon's avatar
qinsoon committed
246
    pub fn exception_restore(dest: Address, callee_saved: *const Word, rsp: Address) -> !;
247 248
}

qinsoon's avatar
qinsoon committed
249
impl MuThread {
250
    pub fn new(id: MuID, allocator: mm::Mutator, stack: Box<MuStack>, user_tls: Option<Address>, vm: Arc<VM>) -> MuThread {
qinsoon's avatar
qinsoon committed
251 252 253 254
        MuThread {
            hdr: MuEntityHeader::unnamed(id),
            allocator: allocator,
            stack: Some(stack),
255
            native_sp_loc: unsafe {Address::zero()},
256 257 258
            user_tls: user_tls,
            vm: vm,
            exception_obj: unsafe {Address::zero()}
qinsoon's avatar
qinsoon committed
259 260
        }
    }
qinsoon's avatar
qinsoon committed
261
    
qinsoon's avatar
qinsoon committed
262
    #[inline(always)]
263 264
    pub fn current() -> &'static MuThread {
        unsafe{
qinsoon's avatar
qinsoon committed
265
            muentry_get_thread_local().to_ptr::<MuThread>().as_ref().unwrap()
qinsoon's avatar
qinsoon committed
266 267 268
        }
    }
    
qinsoon's avatar
qinsoon committed
269
    #[inline(always)]
270 271
    pub fn current_mut() -> &'static mut MuThread {
        unsafe{
qinsoon's avatar
qinsoon committed
272
            muentry_get_thread_local().to_ptr_mut::<MuThread>().as_mut().unwrap()
273 274
        }
    }
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340

    pub unsafe fn current_thread_as_mu_thread(threadlocal: Address, vm: Arc<VM>) {
        use std::usize;

        let user_tls = {
            if threadlocal.is_zero() {
                None
            } else {
                Some(threadlocal)
            }
        };

        // fake a stack for current thread
        let fake_mu_stack_for_cur = Box::new(MuStack {
            hdr: MuEntityHeader::unnamed(vm.next_id()),
            func: None,

            state: MuStackState::Active,

            // we do not know anything about current stack
            // treat it as max size
            size: usize::MAX,
            overflow_guard: unsafe {Address::zero()},
            lower_bound: unsafe {Address::zero()},
            upper_bound: unsafe {Address::max()},
            underflow_guard: unsafe {Address::max()},

            // these will only be used when stack is not active (we still save to these fields)
            // their values do not matter now
            sp: unsafe {Address::zero()},
            bp: unsafe {Address::zero()},
            ip: unsafe {Address::zero()},

            // we are not responsible for keeping the memory alive
            mmap: None,
        });

        let fake_mu_thread = MuThread {
            hdr: MuEntityHeader::unnamed(vm.next_id()),

            // valid allocator and stack
            allocator: mm::new_mutator(),
            stack: Some(fake_mu_stack_for_cur),

            // we do not need native_sp_loc (we do not expect the thread to call
            native_sp_loc: unsafe {Address::zero()},
            user_tls: user_tls,

            vm: vm,
            exception_obj: unsafe {Address::zero()}
        };

        let ptr_fake_mu_thread : *mut MuThread = Box::into_raw(Box::new(fake_mu_thread));

        // set thread local
        unsafe {set_thread_local(ptr_fake_mu_thread)};

//        let addr = unsafe {muentry_get_thread_local()};
//        let sp_threadlocal_loc = addr.plus(*NATIVE_SP_LOC_OFFSET);
//
//        unsafe {
//            fake_swap_mu_thread(sp_threadlocal_loc);
//        }

        debug!("returned to Rust thread (fake muthread)");
    }
341 342 343 344 345 346 347 348 349 350 351 352 353 354
    
    pub fn new_thread_normal(mut stack: Box<MuStack>, threadlocal: Address, vals: Vec<ValueLocation>, vm: Arc<VM>) -> JoinHandle<()> {
        let user_tls = {
            if threadlocal.is_zero() {
                None
            } else {
                Some(threadlocal)
            }
        };
        
        // set up arguments on stack
        stack.runtime_load_args(vals);
        
        MuThread::mu_thread_launch(vm.next_id(), stack, user_tls, vm)
355
    }
356
    
357
    #[no_mangle]
qinsoon's avatar
qinsoon committed
358
    #[allow(unused_variables)]
359
    pub extern fn mu_thread_launch(id: MuID, stack: Box<MuStack>, user_tls: Option<Address>, vm: Arc<VM>) -> JoinHandle<()> {
360
        let new_sp = stack.sp;
361
        let entry = runtime::resolve_symbol(vm.name_of(stack.func.as_ref().unwrap().1));
362 363
        debug!("entry : 0x{:x}", entry);
        
qinsoon's avatar
qinsoon committed
364
        match thread::Builder::new().name(format!("Mu Thread #{}", id)).spawn(move || {
365
            let muthread : *mut MuThread = Box::into_raw(Box::new(MuThread::new(id, mm::new_mutator(), stack, user_tls, vm)));
366 367
            
            // set thread local
368 369
            unsafe {set_thread_local(muthread)};
            
qinsoon's avatar
qinsoon committed
370
            let addr = unsafe {muentry_get_thread_local()};
371
            let sp_threadlocal_loc = addr.plus(*NATIVE_SP_LOC_OFFSET);
372 373 374
            
            debug!("new sp: 0x{:x}", new_sp);
            debug!("sp_store: 0x{:x}", sp_threadlocal_loc);
375
            
376 377 378
            unsafe {
                swap_to_mu_stack(new_sp, entry, sp_threadlocal_loc); 
            }
379 380
            
            debug!("returned to Rust stack. Going to quit");
qinsoon's avatar
qinsoon committed
381 382 383 384 385
        }) {
            Ok(handle) => handle,
            Err(_) => panic!("failed to create a thread")
        }
    }
386 387
}

qinsoon's avatar
qinsoon committed
388
#[derive(Debug, RustcEncodable, RustcDecodable)]
389 390 391
pub struct MuPrimordialThread {
    pub func_id: MuID,
    pub args: Vec<Constant>
Kunshan Wang's avatar
Kunshan Wang committed
392
}